Merge pull request #2273 from MediaBrowser/dev

Dev
This commit is contained in:
Luke 2016-11-04 10:10:05 -04:00 committed by GitHub
commit ca653f00e6
63 changed files with 1422 additions and 1118 deletions

View File

@ -28,11 +28,13 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using Emby.Common.Implementations.Cryptography; using Emby.Common.Implementations.Cryptography;
using Emby.Common.Implementations.Diagnostics; using Emby.Common.Implementations.Diagnostics;
using Emby.Common.Implementations.Net;
using Emby.Common.Implementations.Threading; using Emby.Common.Implementations.Threading;
using MediaBrowser.Common; using MediaBrowser.Common;
using MediaBrowser.Common.IO; using MediaBrowser.Common.IO;
using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Threading; using MediaBrowser.Model.Threading;
@ -153,6 +155,7 @@ namespace Emby.Common.Implementations
protected IProcessFactory ProcessFactory { get; private set; } protected IProcessFactory ProcessFactory { get; private set; }
protected ITimerFactory TimerFactory { get; private set; } protected ITimerFactory TimerFactory { get; private set; }
protected ISocketFactory SocketFactory { get; private set; }
/// <summary> /// <summary>
/// Gets the name. /// Gets the name.
@ -549,6 +552,9 @@ return null;
TimerFactory = new TimerFactory(); TimerFactory = new TimerFactory();
RegisterSingleInstance(TimerFactory); RegisterSingleInstance(TimerFactory);
SocketFactory = new SocketFactory(null);
RegisterSingleInstance(SocketFactory);
RegisterSingleInstance(CryptographyProvider); RegisterSingleInstance(CryptographyProvider);
return Task.FromResult(true); return Task.FromResult(true);

View File

@ -641,6 +641,16 @@ namespace Emby.Common.Implementations.IO
}).Where(i => i != null); }).Where(i => i != null);
} }
public string[] ReadAllLines(string path)
{
return File.ReadAllLines(path);
}
public void WriteAllLines(string path, IEnumerable<string> lines)
{
File.WriteAllLines(path, lines);
}
public Stream OpenRead(string path) public Stream OpenRead(string path)
{ {
return File.OpenRead(path); return File.OpenRead(path);

View File

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Emby.Common.Implementations.Net
{
/// <summary>
/// Correclty implements the <see cref="IDisposable"/> interface and pattern for an object containing only managed resources, and adds a few common niceities not on the interface such as an <see cref="IsDisposed"/> property.
/// </summary>
public abstract class DisposableManagedObjectBase : IDisposable
{
#region Public Methods
/// <summary>
/// Override this method and dispose any objects you own the lifetime of if disposing is true;
/// </summary>
/// <param name="disposing">True if managed objects should be disposed, if false, only unmanaged resources should be released.</param>
protected abstract void Dispose(bool disposing);
/// <summary>
/// Throws and <see cref="System.ObjectDisposedException"/> if the <see cref="IsDisposed"/> property is true.
/// </summary>
/// <seealso cref="IsDisposed"/>
/// <exception cref="System.ObjectDisposedException">Thrown if the <see cref="IsDisposed"/> property is true.</exception>
/// <seealso cref="Dispose()"/>
protected virtual void ThrowIfDisposed()
{
if (this.IsDisposed) throw new ObjectDisposedException(this.GetType().FullName);
}
#endregion
#region Public Properties
/// <summary>
/// Sets or returns a boolean indicating whether or not this instance has been disposed.
/// </summary>
/// <seealso cref="Dispose()"/>
public bool IsDisposed
{
get;
private set;
}
#endregion
#region IDisposable Members
/// <summary>
/// Disposes this object instance and all internally managed resources.
/// </summary>
/// <remarks>
/// <para>Sets the <see cref="IsDisposed"/> property to true. Does not explicitly throw an exception if called multiple times, but makes no promises about behaviour of derived classes.</para>
/// </remarks>
/// <seealso cref="IsDisposed"/>
public void Dispose()
{
try
{
IsDisposed = true;
Dispose(true);
}
finally
{
GC.SuppressFinalize(this);
}
}
#endregion
}
}

View File

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using MediaBrowser.Model.Net;
namespace Emby.Common.Implementations.Net
{
public class SocketFactory : ISocketFactory
{
// THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS
// Be careful to check any changes compile and work for all platform projects it is shared in.
// Not entirely happy with this. Would have liked to have done something more generic/reusable,
// but that wasn't really the point so kept to YAGNI principal for now, even if the
// interfaces are a bit ugly, specific and make assumptions.
/// <summary>
/// Used by RSSDP components to create implementations of the <see cref="IUdpSocket"/> interface, to perform platform agnostic socket communications.
/// </summary>
private IPAddress _LocalIP;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="localIP">A string containing the IP address of the local network adapter to bind sockets to. Null or empty string will use <see cref="IPAddress.Any"/>.</param>
public SocketFactory(string localIP)
{
if (String.IsNullOrEmpty(localIP))
_LocalIP = IPAddress.Any;
else
_LocalIP = IPAddress.Parse(localIP);
}
#region ISocketFactory Members
/// <summary>
/// Creates a new UDP socket that is a member of the SSDP multicast local admin group and binds it to the specified local port.
/// </summary>
/// <param name="localPort">An integer specifying the local port to bind the socket to.</param>
/// <returns>An implementation of the <see cref="IUdpSocket"/> interface used by RSSDP components to perform socket operations.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The purpose of this method is to create and returns a disposable result, it is up to the caller to dispose it when they are done with it.")]
public IUdpSocket CreateUdpSocket(int localPort)
{
if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort");
var retVal = new Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
try
{
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), _LocalIP));
return new UdpSocket(retVal, localPort, _LocalIP.ToString());
}
catch
{
if (retVal != null)
retVal.Dispose();
throw;
}
}
/// <summary>
/// Creates a new UDP socket that is a member of the specified multicast IP address, and binds it to the specified local port.
/// </summary>
/// <param name="ipAddress">The multicast IP address to make the socket a member of.</param>
/// <param name="multicastTimeToLive">The multicast time to live value for the socket.</param>
/// <param name="localPort">The number of the local port to bind to.</param>
/// <returns></returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "ip"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The purpose of this method is to create and returns a disposable result, it is up to the caller to dispose it when they are done with it.")]
public IUdpSocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort)
{
if (ipAddress == null) throw new ArgumentNullException("ipAddress");
if (ipAddress.Length == 0) throw new ArgumentException("ipAddress cannot be an empty string.", "ipAddress");
if (multicastTimeToLive <= 0) throw new ArgumentException("multicastTimeToLive cannot be zero or less.", "multicastTimeToLive");
if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort");
var retVal = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
try
{
#if NETSTANDARD1_3
// The ExclusiveAddressUse socket option is a Windows-specific option that, when set to "true," tells Windows not to allow another socket to use the same local address as this socket
// See https://github.com/dotnet/corefx/pull/11509 for more details
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
{
retVal.ExclusiveAddressUse = false;
}
#else
retVal.ExclusiveAddressUse = false;
#endif
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse(ipAddress), _LocalIP));
retVal.MulticastLoopback = true;
return new UdpSocket(retVal, localPort, _LocalIP.ToString());
}
catch
{
if (retVal != null)
retVal.Dispose();
throw;
}
}
#endregion
}
}

View File

@ -1,15 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security; using System.Security;
using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Rssdp.Infrastructure; using MediaBrowser.Model.Net;
namespace Rssdp namespace Emby.Common.Implementations.Net
{ {
// THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS // THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS
// Be careful to check any changes compile and work for all platform projects it is shared in. // Be careful to check any changes compile and work for all platform projects it is shared in.
@ -48,7 +46,7 @@ namespace Rssdp
#region IUdpSocket Members #region IUdpSocket Members
public System.Threading.Tasks.Task<ReceivedUdpData> ReceiveAsync() public Task<ReceivedUdpData> ReceiveAsync()
{ {
ThrowIfDisposed(); ThrowIfDisposed();
@ -76,7 +74,7 @@ namespace Rssdp
return tcs.Task; return tcs.Task;
} }
public Task SendTo(byte[] messageData, UdpEndPoint endPoint) public Task SendTo(byte[] messageData, IpEndPointInfo endPoint)
{ {
ThrowIfDisposed(); ThrowIfDisposed();
@ -84,14 +82,14 @@ namespace Rssdp
if (endPoint == null) throw new ArgumentNullException("endPoint"); if (endPoint == null) throw new ArgumentNullException("endPoint");
#if NETSTANDARD1_6 #if NETSTANDARD1_6
_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(endPoint.IPAddress), endPoint.Port)); _Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(endPoint.IpAddress.ToString()), endPoint.Port));
return Task.FromResult(true); return Task.FromResult(true);
#else #else
var taskSource = new TaskCompletionSource<bool>(); var taskSource = new TaskCompletionSource<bool>();
try try
{ {
_Socket.BeginSendTo(messageData, 0, messageData.Length, SocketFlags.None, new System.Net.IPEndPoint(IPAddress.Parse(endPoint.IPAddress), endPoint.Port), result => _Socket.BeginSendTo(messageData, 0, messageData.Length, SocketFlags.None, new System.Net.IPEndPoint(IPAddress.Parse(endPoint.IpAddress.ToString()), endPoint.Port), result =>
{ {
try try
{ {
@ -166,11 +164,7 @@ namespace Rssdp
{ {
Buffer = state.Buffer, Buffer = state.Buffer,
ReceivedBytes = bytesRead, ReceivedBytes = bytesRead,
ReceivedFrom = new UdpEndPoint() ReceivedFrom = ToIpEndPointInfo(ipEndPoint)
{
IPAddress = ipEndPoint.Address.ToString(),
Port = ipEndPoint.Port
}
} }
); );
} }
@ -191,6 +185,25 @@ namespace Rssdp
} }
} }
private static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint)
{
if (endpoint == null)
{
return null;
}
return new IpEndPointInfo
{
IpAddress = new IpAddressInfo
{
Address = endpoint.Address.ToString(),
IsIpv6 = endpoint.AddressFamily == AddressFamily.InterNetworkV6
},
Port = endpoint.Port
};
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions via task methods should be reported by task completion source, so this should be ok.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions via task methods should be reported by task completion source, so this should be ok.")]
private void ProcessResponse(IAsyncResult asyncResult) private void ProcessResponse(IAsyncResult asyncResult)
{ {
@ -206,11 +219,7 @@ namespace Rssdp
{ {
Buffer = state.Buffer, Buffer = state.Buffer,
ReceivedBytes = bytesRead, ReceivedBytes = bytesRead,
ReceivedFrom = new UdpEndPoint() ReceivedFrom = ToIpEndPointInfo(ipEndPoint)
{
IPAddress = ipEndPoint.Address.ToString(),
Port = ipEndPoint.Port
}
} }
); );
} }
@ -245,7 +254,7 @@ namespace Rssdp
} }
public EndPoint EndPoint; public EndPoint EndPoint;
public byte[] Buffer = new byte[SsdpConstants.DefaultUdpSocketBufferSize]; public byte[] Buffer = new byte[8192];
public System.Net.Sockets.Socket Socket { get; private set; } public System.Net.Sockets.Socket Socket { get; private set; }

View File

@ -14,5 +14,13 @@ namespace Emby.Common.Implementations.Reflection
#endif #endif
return type.GetTypeInfo().Assembly.GetManifestResourceStream(resource); return type.GetTypeInfo().Assembly.GetManifestResourceStream(resource);
} }
public string[] GetManifestResourceNames(Type type)
{
#if NET46
return type.Assembly.GetManifestResourceNames();
#endif
return type.GetTypeInfo().Assembly.GetManifestResourceNames();
}
} }
} }

View File

@ -4,6 +4,7 @@ using MediaBrowser.Controller.Dlna;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System.Collections.Generic; using System.Collections.Generic;
using MediaBrowser.Model.Xml;
namespace Emby.Dlna.ConnectionManager namespace Emby.Dlna.ConnectionManager
{ {
@ -12,13 +13,15 @@ namespace Emby.Dlna.ConnectionManager
private readonly IDlnaManager _dlna; private readonly IDlnaManager _dlna;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient) public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(logger, httpClient) : base(logger, httpClient)
{ {
_dlna = dlna; _dlna = dlna;
_config = config; _config = config;
_logger = logger; _logger = logger;
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
} }
public string GetServiceXml(IDictionary<string, string> headers) public string GetServiceXml(IDictionary<string, string> headers)
@ -31,7 +34,7 @@ namespace Emby.Dlna.ConnectionManager
var profile = _dlna.GetProfile(request.Headers) ?? var profile = _dlna.GetProfile(request.Headers) ??
_dlna.GetDefaultProfile(); _dlna.GetDefaultProfile();
return new ControlHandler(_logger, profile, _config).ProcessControlRequest(request); return new ControlHandler(_config, _logger, XmlReaderSettingsFactory, profile).ProcessControlRequest(request);
} }
} }
} }

View File

@ -6,6 +6,7 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using MediaBrowser.Model.Xml;
namespace Emby.Dlna.ConnectionManager namespace Emby.Dlna.ConnectionManager
{ {
@ -13,12 +14,6 @@ namespace Emby.Dlna.ConnectionManager
{ {
private readonly DeviceProfile _profile; 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) protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
{ {
if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase))
@ -37,5 +32,10 @@ namespace Emby.Dlna.ConnectionManager
{ "Sink", "" } { "Sink", "" }
}; };
} }
public ControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory, DeviceProfile profile) : base(config, logger, xmlReaderSettingsFactory)
{
_profile = profile;
}
} }
} }

View File

@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Xml;
namespace Emby.Dlna.ContentDirectory namespace Emby.Dlna.ContentDirectory
{ {
@ -29,6 +30,7 @@ namespace Emby.Dlna.ContentDirectory
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly IUserViewManager _userViewManager; private readonly IUserViewManager _userViewManager;
private readonly Func<IMediaEncoder> _mediaEncoder; private readonly Func<IMediaEncoder> _mediaEncoder;
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
public ContentDirectory(IDlnaManager dlna, public ContentDirectory(IDlnaManager dlna,
IUserDataManager userDataManager, IUserDataManager userDataManager,
@ -37,7 +39,7 @@ namespace Emby.Dlna.ContentDirectory
IServerConfigurationManager config, IServerConfigurationManager config,
IUserManager userManager, IUserManager userManager,
ILogger logger, ILogger logger,
IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, Func<IMediaEncoder> mediaEncoder) IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, Func<IMediaEncoder> mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(logger, httpClient) : base(logger, httpClient)
{ {
_dlna = dlna; _dlna = dlna;
@ -51,6 +53,7 @@ namespace Emby.Dlna.ContentDirectory
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_userViewManager = userViewManager; _userViewManager = userViewManager;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
} }
private int SystemUpdateId private int SystemUpdateId
@ -93,7 +96,8 @@ namespace Emby.Dlna.ContentDirectory
_channelManager, _channelManager,
_mediaSourceManager, _mediaSourceManager,
_userViewManager, _userViewManager,
_mediaEncoder()) _mediaEncoder(),
XmlReaderSettingsFactory)
.ProcessControlRequest(request); .ProcessControlRequest(request);
} }

View File

@ -17,6 +17,7 @@ using MediaBrowser.Model.Querying;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -24,6 +25,7 @@ using System.Threading.Tasks;
using System.Xml; using System.Xml;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Xml;
namespace Emby.Dlna.ContentDirectory namespace Emby.Dlna.ContentDirectory
{ {
@ -35,7 +37,6 @@ namespace Emby.Dlna.ContentDirectory
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly User _user; private readonly User _user;
private readonly IUserViewManager _userViewManager; private readonly IUserViewManager _userViewManager;
private readonly IMediaEncoder _mediaEncoder;
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/";
@ -49,8 +50,8 @@ namespace Emby.Dlna.ContentDirectory
private readonly DeviceProfile _profile; private readonly DeviceProfile _profile;
public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder) public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(config, logger) : base(config, logger, xmlReaderSettingsFactory)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_userDataManager = userDataManager; _userDataManager = userDataManager;
@ -58,11 +59,10 @@ namespace Emby.Dlna.ContentDirectory
_systemUpdateId = systemUpdateId; _systemUpdateId = systemUpdateId;
_channelManager = channelManager; _channelManager = channelManager;
_userViewManager = userViewManager; _userViewManager = userViewManager;
_mediaEncoder = mediaEncoder;
_profile = profile; _profile = profile;
_config = config; _config = config;
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, libraryManager, _mediaEncoder); _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, libraryManager, mediaEncoder);
} }
protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams) protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
@ -209,73 +209,92 @@ namespace Emby.Dlna.ContentDirectory
start = startVal; start = startVal;
} }
//var root = GetItem(id) as IMediaFolder; var settings = new XmlWriterSettings
var result = new XmlDocument(); {
Encoding = Encoding.UTF8,
CloseOutput = false,
OmitXmlDeclaration = true,
ConformanceLevel = ConformanceLevel.Fragment
};
var didl = result.CreateElement(string.Empty, "DIDL-Lite", NS_DIDL); StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
didl.SetAttribute("xmlns:dc", NS_DC);
didl.SetAttribute("xmlns:dlna", NS_DLNA);
didl.SetAttribute("xmlns:upnp", NS_UPNP);
//didl.SetAttribute("xmlns:sec", NS_SEC);
result.AppendChild(didl);
var serverItem = GetItemFromObjectId(id, user);
var item = serverItem.Item;
int totalCount; int totalCount;
if (string.Equals(flag, "BrowseMetadata")) using (XmlWriter writer = XmlWriter.Create(builder, settings))
{ {
totalCount = 1; //writer.WriteStartDocument();
if (item.IsFolder || serverItem.StubType.HasValue) writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
{
var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id)); writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
} writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
else writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
//didl.SetAttribute("xmlns:sec", NS_SEC);
foreach (var att in _profile.XmlRootAttributes)
{ {
result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(_config.GetDlnaConfiguration(), result, item, null, null, deviceId, filter)); writer.WriteAttributeString(att.Name, att.Value);
} }
provided++; var serverItem = GetItemFromObjectId(id, user);
} var item = serverItem.Item;
else
{
var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
totalCount = childrenResult.TotalRecordCount;
provided = childrenResult.Items.Length; if (string.Equals(flag, "BrowseMetadata"))
foreach (var i in childrenResult.Items)
{ {
var childItem = i.Item; totalCount = 1;
var displayStubType = i.StubType;
if (childItem.IsFolder || displayStubType.HasValue) if (item.IsFolder || serverItem.StubType.HasValue)
{ {
var childCount = (await GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false)) var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
.TotalRecordCount;
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, childItem, displayStubType, item, childCount, filter)); _didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
} }
else else
{ {
result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(_config.GetDlnaConfiguration(), result, childItem, item, serverItem.StubType, deviceId, filter)); _didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, item, null, null, deviceId, filter);
}
provided++;
}
else
{
var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
totalCount = childrenResult.TotalRecordCount;
provided = childrenResult.Items.Length;
foreach (var i in childrenResult.Items)
{
var childItem = i.Item;
var displayStubType = i.StubType;
if (childItem.IsFolder || displayStubType.HasValue)
{
var childCount = (await GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false))
.TotalRecordCount;
_didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter);
}
else
{
_didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, childItem, item, serverItem.StubType, deviceId, filter);
}
} }
} }
writer.WriteEndElement();
//writer.WriteEndDocument();
} }
var resXML = result.OuterXml; var resXML = builder.ToString();
return new List<KeyValuePair<string, string>> return new List<KeyValuePair<string, string>>
{ {
new KeyValuePair<string,string>("Result", resXML), new KeyValuePair<string,string>("Result", resXML),
new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)), new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)), new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture)) new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
}; };
} }
private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(Headers sparams, User user, string deviceId) private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(Headers sparams, User user, string deviceId)
@ -303,55 +322,72 @@ namespace Emby.Dlna.ContentDirectory
start = startVal; start = startVal;
} }
//var root = GetItem(id) as IMediaFolder; var settings = new XmlWriterSettings
var result = new XmlDocument();
var didl = result.CreateElement(string.Empty, "DIDL-Lite", NS_DIDL);
didl.SetAttribute("xmlns:dc", NS_DC);
didl.SetAttribute("xmlns:dlna", NS_DLNA);
didl.SetAttribute("xmlns:upnp", NS_UPNP);
foreach (var att in _profile.XmlRootAttributes)
{ {
didl.SetAttribute(att.Name, att.Value); Encoding = Encoding.UTF8,
CloseOutput = false,
OmitXmlDeclaration = true,
ConformanceLevel = ConformanceLevel.Fragment
};
StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
int totalCount = 0;
int provided = 0;
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
//writer.WriteStartDocument();
writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
//didl.SetAttribute("xmlns:sec", NS_SEC);
foreach (var att in _profile.XmlRootAttributes)
{
writer.WriteAttributeString(att.Name, att.Value);
}
var serverItem = GetItemFromObjectId(sparams["ContainerID"], user);
var item = serverItem.Item;
var childrenResult = (await GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requestedCount).ConfigureAwait(false));
totalCount = childrenResult.TotalRecordCount;
provided = childrenResult.Items.Length;
foreach (var i in childrenResult.Items)
{
if (i.IsFolder)
{
var childCount = (await GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false))
.TotalRecordCount;
_didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter);
}
else
{
_didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, i, item, serverItem.StubType, deviceId, filter);
}
}
writer.WriteEndElement();
//writer.WriteEndDocument();
} }
result.AppendChild(didl); var resXML = builder.ToString();
var serverItem = GetItemFromObjectId(sparams["ContainerID"], user);
var item = serverItem.Item;
var childrenResult = (await GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requestedCount).ConfigureAwait(false));
var totalCount = childrenResult.TotalRecordCount;
var provided = childrenResult.Items.Length;
foreach (var i in childrenResult.Items)
{
if (i.IsFolder)
{
var childCount = (await GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false))
.TotalRecordCount;
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, i, null, item, childCount, filter));
}
else
{
result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(_config.GetDlnaConfiguration(), result, i, item, serverItem.StubType, deviceId, filter));
}
}
var resXML = result.OuterXml;
return new List<KeyValuePair<string, string>> return new List<KeyValuePair<string, string>>
{ {
new KeyValuePair<string,string>("Result", resXML), new KeyValuePair<string,string>("Result", resXML),
new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)), new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)), new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture)) new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
}; };
} }
private Task<QueryResult<BaseItem>> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit) private Task<QueryResult<BaseItem>> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)

View File

@ -17,6 +17,7 @@ using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Xml; using System.Xml;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
@ -62,50 +63,66 @@ namespace Emby.Dlna.Didl
public string GetItemDidl(DlnaOptions options, BaseItem item, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo) public string GetItemDidl(DlnaOptions options, BaseItem item, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
{ {
var result = new XmlDocument(); var settings = new XmlWriterSettings
var didl = result.CreateElement(string.Empty, "DIDL-Lite", NS_DIDL);
didl.SetAttribute("xmlns:dc", NS_DC);
didl.SetAttribute("xmlns:dlna", NS_DLNA);
didl.SetAttribute("xmlns:upnp", NS_UPNP);
//didl.SetAttribute("xmlns:sec", NS_SEC);
foreach (var att in _profile.XmlRootAttributes)
{ {
didl.SetAttribute(att.Name, att.Value); Encoding = Encoding.UTF8,
CloseOutput = false,
OmitXmlDeclaration = true,
ConformanceLevel = ConformanceLevel.Fragment
};
StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
//writer.WriteStartDocument();
writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
//didl.SetAttribute("xmlns:sec", NS_SEC);
foreach (var att in _profile.XmlRootAttributes)
{
writer.WriteAttributeString(att.Name, att.Value);
}
WriteItemElement(options, writer, item, context, null, deviceId, filter, streamInfo);
writer.WriteEndElement();
//writer.WriteEndDocument();
} }
result.AppendChild(didl); return builder.ToString();
result.DocumentElement.AppendChild(GetItemElement(options, result, item, context, null, deviceId, filter, streamInfo));
return result.DocumentElement.OuterXml;
} }
public XmlElement GetItemElement(DlnaOptions options, XmlDocument doc, BaseItem item, BaseItem context, StubType? contextStubType, string deviceId, Filter filter, StreamInfo streamInfo = null) public void WriteItemElement(DlnaOptions options, XmlWriter writer, BaseItem item, BaseItem context, StubType? contextStubType, string deviceId, Filter filter, StreamInfo streamInfo = null)
{ {
var clientId = GetClientId(item, null); var clientId = GetClientId(item, null);
var element = doc.CreateElement(string.Empty, "item", NS_DIDL); writer.WriteStartElement(string.Empty, "item", NS_DIDL);
element.SetAttribute("restricted", "1");
element.SetAttribute("id", clientId); writer.WriteAttributeString("restricted", "1");
writer.WriteAttributeString("id", clientId);
if (context != null) if (context != null)
{ {
element.SetAttribute("parentID", GetClientId(context, contextStubType)); writer.WriteAttributeString("parentID", GetClientId(context, contextStubType));
} }
else else
{ {
var parent = item.DisplayParentId; var parent = item.DisplayParentId;
if (parent.HasValue) if (parent.HasValue)
{ {
element.SetAttribute("parentID", GetClientId(parent.Value, null)); writer.WriteAttributeString("parentID", GetClientId(parent.Value, null));
} }
} }
//AddBookmarkInfo(item, user, element); //AddBookmarkInfo(item, user, element);
AddGeneralProperties(item, null, context, element, filter); AddGeneralProperties(item, null, context, writer, filter);
// refID? // refID?
// storeAttribute(itemNode, object, ClassProperties.REF_ID, false); // storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
@ -116,17 +133,16 @@ namespace Emby.Dlna.Didl
{ {
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{ {
AddAudioResource(options, element, hasMediaSources, deviceId, filter, streamInfo); AddAudioResource(options, writer, hasMediaSources, deviceId, filter, streamInfo);
} }
else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{ {
AddVideoResource(options, element, hasMediaSources, deviceId, filter, streamInfo); AddVideoResource(options, writer, hasMediaSources, deviceId, filter, streamInfo);
} }
} }
AddCover(item, context, null, element); AddCover(item, context, null, writer);
writer.WriteEndElement();
return element;
} }
private ILogger GetStreamBuilderLogger(DlnaOptions options) private ILogger GetStreamBuilderLogger(DlnaOptions options)
@ -139,7 +155,7 @@ namespace Emby.Dlna.Didl
return new NullLogger(); return new NullLogger();
} }
private void AddVideoResource(DlnaOptions options, XmlElement container, IHasMediaSources video, string deviceId, Filter filter, StreamInfo streamInfo = null) private void AddVideoResource(DlnaOptions options, XmlWriter writer, IHasMediaSources video, string deviceId, Filter filter, StreamInfo streamInfo = null)
{ {
if (streamInfo == null) if (streamInfo == null)
{ {
@ -182,14 +198,14 @@ namespace Emby.Dlna.Didl
foreach (var contentFeature in contentFeatureList) foreach (var contentFeature in contentFeatureList)
{ {
AddVideoResource(container, video, deviceId, filter, contentFeature, streamInfo); AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo);
} }
foreach (var subtitle in streamInfo.GetSubtitleProfiles(false, _serverAddress, _accessToken)) foreach (var subtitle in streamInfo.GetSubtitleProfiles(false, _serverAddress, _accessToken))
{ {
if (subtitle.DeliveryMethod == SubtitleDeliveryMethod.External) if (subtitle.DeliveryMethod == SubtitleDeliveryMethod.External)
{ {
var subtitleAdded = AddSubtitleElement(container, subtitle); var subtitleAdded = AddSubtitleElement(writer, subtitle);
if (subtitleAdded && _profile.EnableSingleSubtitleLimit) if (subtitleAdded && _profile.EnableSingleSubtitleLimit)
{ {
@ -199,7 +215,7 @@ namespace Emby.Dlna.Didl
} }
} }
private bool AddSubtitleElement(XmlElement container, SubtitleStreamInfo info) private bool AddSubtitleElement(XmlWriter writer, SubtitleStreamInfo info)
{ {
var subtitleProfile = _profile.SubtitleProfiles var subtitleProfile = _profile.SubtitleProfiles
.FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase) && i.Method == SubtitleDeliveryMethod.External); .FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase) && i.Method == SubtitleDeliveryMethod.External);
@ -216,52 +232,45 @@ namespace Emby.Dlna.Didl
// <sec:CaptionInfoEx sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfoEx> // <sec:CaptionInfoEx sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfoEx>
// <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo> // <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo>
var res = container.OwnerDocument.CreateElement("CaptionInfoEx", "sec"); writer.WriteStartElement("sec", "CaptionInfoEx", null);
writer.WriteAttributeString("sec", "type", null, info.Format.ToLower());
res.InnerText = info.Url; writer.WriteString(info.Url);
writer.WriteEndElement();
//// TODO: attribute needs SEC:
res.SetAttribute("type", "sec", info.Format.ToLower());
container.AppendChild(res);
} }
else if (string.Equals(subtitleMode, "smi", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(subtitleMode, "smi", StringComparison.OrdinalIgnoreCase))
{ {
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL); writer.WriteStartElement(string.Empty, "res", NS_DIDL);
res.InnerText = info.Url; writer.WriteAttributeString("protocolInfo", "http-get:*:smi/caption:*");
res.SetAttribute("protocolInfo", "http-get:*:smi/caption:*"); writer.WriteString(info.Url);
writer.WriteEndElement();
container.AppendChild(res);
} }
else else
{ {
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL); writer.WriteStartElement(string.Empty, "res", NS_DIDL);
res.InnerText = info.Url;
var protocolInfo = string.Format("http-get:*:text/{0}:*", info.Format.ToLower()); var protocolInfo = string.Format("http-get:*:text/{0}:*", info.Format.ToLower());
res.SetAttribute("protocolInfo", protocolInfo); writer.WriteAttributeString("protocolInfo", protocolInfo);
container.AppendChild(res); writer.WriteString(info.Url);
writer.WriteEndElement();
} }
return true; return true;
} }
private void AddVideoResource(XmlElement container, IHasMediaSources video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo) private void AddVideoResource(XmlWriter writer, IHasMediaSources video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)
{ {
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL); writer.WriteStartElement(string.Empty, "res", NS_DIDL);
var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken); var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken);
res.InnerText = url;
var mediaSource = streamInfo.MediaSource; var mediaSource = streamInfo.MediaSource;
if (mediaSource.RunTimeTicks.HasValue) if (mediaSource.RunTimeTicks.HasValue)
{ {
res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture)); writer.WriteAttributeString("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
} }
if (filter.Contains("res@size")) if (filter.Contains("res@size"))
@ -272,7 +281,7 @@ namespace Emby.Dlna.Didl
if (size.HasValue) if (size.HasValue)
{ {
res.SetAttribute("size", size.Value.ToString(_usCulture)); writer.WriteAttributeString("size", size.Value.ToString(_usCulture));
} }
} }
} }
@ -286,25 +295,25 @@ namespace Emby.Dlna.Didl
if (targetChannels.HasValue) if (targetChannels.HasValue)
{ {
res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture)); writer.WriteAttributeString("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
} }
if (filter.Contains("res@resolution")) if (filter.Contains("res@resolution"))
{ {
if (targetWidth.HasValue && targetHeight.HasValue) if (targetWidth.HasValue && targetHeight.HasValue)
{ {
res.SetAttribute("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value)); writer.WriteAttributeString("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
} }
} }
if (targetSampleRate.HasValue) if (targetSampleRate.HasValue)
{ {
res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture)); writer.WriteAttributeString("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
} }
if (totalBitrate.HasValue) if (totalBitrate.HasValue)
{ {
res.SetAttribute("bitrate", totalBitrate.Value.ToString(_usCulture)); writer.WriteAttributeString("bitrate", totalBitrate.Value.ToString(_usCulture));
} }
var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container, var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container,
@ -332,13 +341,15 @@ namespace Emby.Dlna.Didl
? MimeTypes.GetMimeType(filename) ? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType; : mediaProfile.MimeType;
res.SetAttribute("protocolInfo", String.Format( writer.WriteAttributeString("protocolInfo", String.Format(
"http-get:*:{0}:{1}", "http-get:*:{0}:{1}",
mimeType, mimeType,
contentFeatures contentFeatures
)); ));
container.AppendChild(res); writer.WriteString(url);
writer.WriteEndElement();
} }
private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context) private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
@ -382,32 +393,30 @@ namespace Emby.Dlna.Didl
return item.Name; return item.Name;
} }
private void AddAudioResource(DlnaOptions options, XmlElement container, IHasMediaSources audio, string deviceId, Filter filter, StreamInfo streamInfo = null) private void AddAudioResource(DlnaOptions options, XmlWriter writer, IHasMediaSources audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
{ {
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL); writer.WriteStartElement(string.Empty, "res", NS_DIDL);
if (streamInfo == null) if (streamInfo == null)
{ {
var sources = _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList(); var sources = _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList();
streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions
{ {
ItemId = GetClientId(audio), ItemId = GetClientId(audio),
MediaSources = sources, MediaSources = sources,
Profile = _profile, Profile = _profile,
DeviceId = deviceId DeviceId = deviceId
}); });
} }
var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken); var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken);
res.InnerText = url;
var mediaSource = streamInfo.MediaSource; var mediaSource = streamInfo.MediaSource;
if (mediaSource.RunTimeTicks.HasValue) if (mediaSource.RunTimeTicks.HasValue)
{ {
res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture)); writer.WriteAttributeString("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
} }
if (filter.Contains("res@size")) if (filter.Contains("res@size"))
@ -418,7 +427,7 @@ namespace Emby.Dlna.Didl
if (size.HasValue) if (size.HasValue)
{ {
res.SetAttribute("size", size.Value.ToString(_usCulture)); writer.WriteAttributeString("size", size.Value.ToString(_usCulture));
} }
} }
} }
@ -429,17 +438,17 @@ namespace Emby.Dlna.Didl
if (targetChannels.HasValue) if (targetChannels.HasValue)
{ {
res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture)); writer.WriteAttributeString("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
} }
if (targetSampleRate.HasValue) if (targetSampleRate.HasValue)
{ {
res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture)); writer.WriteAttributeString("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
} }
if (targetAudioBitrate.HasValue) if (targetAudioBitrate.HasValue)
{ {
res.SetAttribute("bitrate", targetAudioBitrate.Value.ToString(_usCulture)); writer.WriteAttributeString("bitrate", targetAudioBitrate.Value.ToString(_usCulture));
} }
var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container, var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container,
@ -462,13 +471,15 @@ namespace Emby.Dlna.Didl
streamInfo.RunTimeTicks, streamInfo.RunTimeTicks,
streamInfo.TranscodeSeekInfo); streamInfo.TranscodeSeekInfo);
res.SetAttribute("protocolInfo", String.Format( writer.WriteAttributeString("protocolInfo", String.Format(
"http-get:*:{0}:{1}", "http-get:*:{0}:{1}",
mimeType, mimeType,
contentFeatures contentFeatures
)); ));
container.AppendChild(res); writer.WriteString(url);
writer.WriteEndElement();
} }
public static bool IsIdRoot(string id) public static bool IsIdRoot(string id)
@ -486,47 +497,48 @@ namespace Emby.Dlna.Didl
return false; return false;
} }
public XmlElement GetFolderElement(XmlDocument doc, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null) public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
{ {
var container = doc.CreateElement(string.Empty, "container", NS_DIDL); writer.WriteStartElement(string.Empty, "container", NS_DIDL);
container.SetAttribute("restricted", "0");
container.SetAttribute("searchable", "1"); writer.WriteAttributeString("restricted", "0");
container.SetAttribute("childCount", childCount.ToString(_usCulture)); writer.WriteAttributeString("searchable", "1");
writer.WriteAttributeString("childCount", childCount.ToString(_usCulture));
var clientId = GetClientId(folder, stubType); var clientId = GetClientId(folder, stubType);
if (string.Equals(requestedId, "0")) if (string.Equals(requestedId, "0"))
{ {
container.SetAttribute("id", "0"); writer.WriteAttributeString("id", "0");
container.SetAttribute("parentID", "-1"); writer.WriteAttributeString("parentID", "-1");
} }
else else
{ {
container.SetAttribute("id", clientId); writer.WriteAttributeString("id", clientId);
if (context != null) if (context != null)
{ {
container.SetAttribute("parentID", GetClientId(context, null)); writer.WriteAttributeString("parentID", GetClientId(context, null));
} }
else else
{ {
var parent = folder.DisplayParentId; var parent = folder.DisplayParentId;
if (!parent.HasValue) if (!parent.HasValue)
{ {
container.SetAttribute("parentID", "0"); writer.WriteAttributeString("parentID", "0");
} }
else else
{ {
container.SetAttribute("parentID", GetClientId(parent.Value, null)); writer.WriteAttributeString("parentID", GetClientId(parent.Value, null));
} }
} }
} }
AddCommonFields(folder, stubType, null, container, filter); AddCommonFields(folder, stubType, null, writer, filter);
AddCover(folder, context, stubType, container); AddCover(folder, context, stubType, writer);
return container; writer.WriteEndElement();
} }
//private void AddBookmarkInfo(BaseItem item, User user, XmlElement element) //private void AddBookmarkInfo(BaseItem item, User user, XmlElement element)
@ -544,27 +556,22 @@ namespace Emby.Dlna.Didl
/// <summary> /// <summary>
/// Adds fields used by both items and folders /// Adds fields used by both items and folders
/// </summary> /// </summary>
/// <param name="item">The item.</param> private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
/// <param name="itemStubType">Type of the item stub.</param>
/// <param name="context">The context.</param>
/// <param name="element">The element.</param>
/// <param name="filter">The filter.</param>
private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlElement element, Filter filter)
{ {
// Don't filter on dc:title because not all devices will include it in the 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 // MediaMonkey for example won't display content without a title
//if (filter.Contains("dc:title")) //if (filter.Contains("dc:title"))
{ {
AddValue(element, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC); AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC);
} }
element.AppendChild(CreateObjectClass(element.OwnerDocument, item, itemStubType)); WriteObjectClass(writer, item, itemStubType);
if (filter.Contains("dc:date")) if (filter.Contains("dc:date"))
{ {
if (item.PremiereDate.HasValue) if (item.PremiereDate.HasValue)
{ {
AddValue(element, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC); AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
} }
} }
@ -572,13 +579,13 @@ namespace Emby.Dlna.Didl
{ {
foreach (var genre in item.Genres) foreach (var genre in item.Genres)
{ {
AddValue(element, "upnp", "genre", genre, NS_UPNP); AddValue(writer, "upnp", "genre", genre, NS_UPNP);
} }
} }
foreach (var studio in item.Studios) foreach (var studio in item.Studios)
{ {
AddValue(element, "upnp", "publisher", studio, NS_UPNP); AddValue(writer, "upnp", "publisher", studio, NS_UPNP);
} }
if (filter.Contains("dc:description")) if (filter.Contains("dc:description"))
@ -592,14 +599,14 @@ namespace Emby.Dlna.Didl
if (!string.IsNullOrWhiteSpace(desc)) if (!string.IsNullOrWhiteSpace(desc))
{ {
AddValue(element, "dc", "description", desc, NS_DC); AddValue(writer, "dc", "description", desc, NS_DC);
} }
} }
if (filter.Contains("upnp:longDescription")) if (filter.Contains("upnp:longDescription"))
{ {
if (!string.IsNullOrWhiteSpace(item.Overview)) if (!string.IsNullOrWhiteSpace(item.Overview))
{ {
AddValue(element, "upnp", "longDescription", item.Overview, NS_UPNP); AddValue(writer, "upnp", "longDescription", item.Overview, NS_UPNP);
} }
} }
@ -607,23 +614,23 @@ namespace Emby.Dlna.Didl
{ {
if (filter.Contains("dc:rating")) if (filter.Contains("dc:rating"))
{ {
AddValue(element, "dc", "rating", item.OfficialRating, NS_DC); AddValue(writer, "dc", "rating", item.OfficialRating, NS_DC);
} }
if (filter.Contains("upnp:rating")) if (filter.Contains("upnp:rating"))
{ {
AddValue(element, "upnp", "rating", item.OfficialRating, NS_UPNP); AddValue(writer, "upnp", "rating", item.OfficialRating, NS_UPNP);
} }
} }
AddPeople(item, element); AddPeople(item, writer);
} }
private XmlElement CreateObjectClass(XmlDocument result, BaseItem item, StubType? stubType) private void WriteObjectClass(XmlWriter writer, BaseItem item, StubType? stubType)
{ {
// More types here // More types here
// http://oss.linn.co.uk/repos/Public/LibUpnpCil/DidlLite/UpnpAv/Test/TestDidlLite.cs // http://oss.linn.co.uk/repos/Public/LibUpnpCil/DidlLite/UpnpAv/Test/TestDidlLite.cs
var objectClass = result.CreateElement("upnp", "class", NS_UPNP); writer.WriteStartElement("upnp", "class", NS_UPNP);
if (item.IsFolder || stubType.HasValue) if (item.IsFolder || stubType.HasValue)
{ {
@ -653,48 +660,48 @@ namespace Emby.Dlna.Didl
} }
} }
objectClass.InnerText = classType ?? "object.container.storageFolder"; writer.WriteString(classType ?? "object.container.storageFolder");
} }
else if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) else if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{ {
objectClass.InnerText = "object.item.audioItem.musicTrack"; writer.WriteString("object.item.audioItem.musicTrack");
} }
else if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase)) else if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
{ {
objectClass.InnerText = "object.item.imageItem.photo"; writer.WriteString("object.item.imageItem.photo");
} }
else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{ {
if (!_profile.RequiresPlainVideoItems && item is Movie) if (!_profile.RequiresPlainVideoItems && item is Movie)
{ {
objectClass.InnerText = "object.item.videoItem.movie"; writer.WriteString("object.item.videoItem.movie");
} }
else if (!_profile.RequiresPlainVideoItems && item is MusicVideo) else if (!_profile.RequiresPlainVideoItems && item is MusicVideo)
{ {
objectClass.InnerText = "object.item.videoItem.musicVideoClip"; writer.WriteString("object.item.videoItem.musicVideoClip");
} }
else else
{ {
objectClass.InnerText = "object.item.videoItem"; writer.WriteString("object.item.videoItem");
} }
} }
else if (item is MusicGenre) else if (item is MusicGenre)
{ {
objectClass.InnerText = _profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre.musicGenre"; writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre.musicGenre");
} }
else if (item is Genre || item is GameGenre) else if (item is Genre || item is GameGenre)
{ {
objectClass.InnerText = _profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre"; writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre");
} }
else else
{ {
objectClass.InnerText = "object.item"; writer.WriteString("object.item");
} }
return objectClass; writer.WriteEndElement();
} }
private void AddPeople(BaseItem item, XmlElement element) private void AddPeople(BaseItem item, XmlWriter writer)
{ {
var types = new[] var types = new[]
{ {
@ -718,7 +725,7 @@ namespace Emby.Dlna.Didl
var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase)) var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
?? PersonType.Actor; ?? PersonType.Actor;
AddValue(element, "upnp", type.ToLower(), actor.Name, NS_UPNP); AddValue(writer, "upnp", type.ToLower(), actor.Name, NS_UPNP);
index++; index++;
@ -729,9 +736,9 @@ namespace Emby.Dlna.Didl
} }
} }
private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlElement element, Filter filter) private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
{ {
AddCommonFields(item, itemStubType, context, element, filter); AddCommonFields(item, itemStubType, context, writer, filter);
var audio = item as Audio; var audio = item as Audio;
@ -739,17 +746,17 @@ namespace Emby.Dlna.Didl
{ {
foreach (var artist in audio.Artists) foreach (var artist in audio.Artists)
{ {
AddValue(element, "upnp", "artist", artist, NS_UPNP); AddValue(writer, "upnp", "artist", artist, NS_UPNP);
} }
if (!string.IsNullOrEmpty(audio.Album)) if (!string.IsNullOrEmpty(audio.Album))
{ {
AddValue(element, "upnp", "album", audio.Album, NS_UPNP); AddValue(writer, "upnp", "album", audio.Album, NS_UPNP);
} }
foreach (var artist in audio.AlbumArtists) foreach (var artist in audio.AlbumArtists)
{ {
AddAlbumArtist(element, artist); AddAlbumArtist(writer, artist);
} }
} }
@ -759,12 +766,12 @@ namespace Emby.Dlna.Didl
{ {
foreach (var artist in album.AlbumArtists) foreach (var artist in album.AlbumArtists)
{ {
AddAlbumArtist(element, artist); AddAlbumArtist(writer, artist);
AddValue(element, "upnp", "artist", artist, NS_UPNP); AddValue(writer, "upnp", "artist", artist, NS_UPNP);
} }
foreach (var artist in album.Artists) foreach (var artist in album.Artists)
{ {
AddValue(element, "upnp", "artist", artist, NS_UPNP); AddValue(writer, "upnp", "artist", artist, NS_UPNP);
} }
} }
@ -774,37 +781,37 @@ namespace Emby.Dlna.Didl
{ {
foreach (var artist in musicVideo.Artists) foreach (var artist in musicVideo.Artists)
{ {
AddValue(element, "upnp", "artist", artist, NS_UPNP); AddValue(writer, "upnp", "artist", artist, NS_UPNP);
AddAlbumArtist(element, artist); AddAlbumArtist(writer, artist);
} }
if (!string.IsNullOrEmpty(musicVideo.Album)) if (!string.IsNullOrEmpty(musicVideo.Album))
{ {
AddValue(element, "upnp", "album", musicVideo.Album, NS_UPNP); AddValue(writer, "upnp", "album", musicVideo.Album, NS_UPNP);
} }
} }
if (item.IndexNumber.HasValue) if (item.IndexNumber.HasValue)
{ {
AddValue(element, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP); AddValue(writer, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
if (item is Episode) if (item is Episode)
{ {
AddValue(element, "upnp", "episodeNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP); AddValue(writer, "upnp", "episodeNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
} }
} }
} }
private void AddAlbumArtist(XmlElement elem, string name) private void AddAlbumArtist(XmlWriter writer, string name)
{ {
try try
{ {
var newNode = elem.OwnerDocument.CreateElement("upnp", "artist", NS_UPNP); writer.WriteStartElement("upnp", "artist", NS_UPNP);
newNode.InnerText = name; writer.WriteAttributeString("role", "AlbumArtist");
newNode.SetAttribute("role", "AlbumArtist"); writer.WriteString(name);
elem.AppendChild(newNode); writer.WriteEndElement();
} }
catch (XmlException) catch (XmlException)
{ {
@ -812,13 +819,11 @@ namespace Emby.Dlna.Didl
} }
} }
private void AddValue(XmlElement elem, string prefix, string name, string value, string namespaceUri) private void AddValue(XmlWriter writer, string prefix, string name, string value, string namespaceUri)
{ {
try try
{ {
var date = elem.OwnerDocument.CreateElement(prefix, name, namespaceUri); writer.WriteElementString(prefix, name, namespaceUri, value);
date.InnerText = value;
elem.AppendChild(date);
} }
catch (XmlException) catch (XmlException)
{ {
@ -826,11 +831,11 @@ namespace Emby.Dlna.Didl
} }
} }
private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlElement element) private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer)
{ {
if (stubType.HasValue && stubType.Value == StubType.People) if (stubType.HasValue && stubType.Value == StubType.People)
{ {
AddEmbeddedImageAsCover("people", element); AddEmbeddedImageAsCover("people", writer);
return; return;
} }
@ -860,8 +865,6 @@ namespace Emby.Dlna.Didl
return; return;
} }
var result = element.OwnerDocument;
var playbackPercentage = 0; var playbackPercentage = 0;
var unplayedCount = 0; var unplayedCount = 0;
@ -891,18 +894,14 @@ namespace Emby.Dlna.Didl
var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, playbackPercentage, unplayedCount, "jpg"); var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, playbackPercentage, unplayedCount, "jpg");
var icon = result.CreateElement("upnp", "albumArtURI", NS_UPNP); writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP);
var profile = result.CreateAttribute("dlna", "profileID", NS_DLNA); writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn);
profile.InnerText = _profile.AlbumArtPn; writer.WriteString(albumartUrlInfo.Url);
icon.SetAttributeNode(profile); writer.WriteEndElement();
icon.InnerText = albumartUrlInfo.Url;
element.AppendChild(icon);
// TOOD: Remove these default values // TOOD: Remove these default values
var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, playbackPercentage, unplayedCount, "jpg"); var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, playbackPercentage, unplayedCount, "jpg");
icon = result.CreateElement("upnp", "icon", NS_UPNP); writer.WriteElementString("upnp", "icon", NS_UPNP, iconUrlInfo.Url);
icon.InnerText = iconUrlInfo.Url;
element.AppendChild(icon);
if (!_profile.EnableAlbumArtInDidl) if (!_profile.EnableAlbumArtInDidl)
{ {
@ -916,36 +915,30 @@ namespace Emby.Dlna.Didl
} }
} }
AddImageResElement(item, element, 160, 160, playbackPercentage, unplayedCount, "jpg", "JPEG_TN"); AddImageResElement(item, writer, 160, 160, playbackPercentage, unplayedCount, "jpg", "JPEG_TN");
if (!_profile.EnableSingleAlbumArtLimit) if (!_profile.EnableSingleAlbumArtLimit)
{ {
AddImageResElement(item, element, 4096, 4096, playbackPercentage, unplayedCount, "jpg", "JPEG_LRG"); AddImageResElement(item, writer, 4096, 4096, playbackPercentage, unplayedCount, "jpg", "JPEG_LRG");
AddImageResElement(item, element, 1024, 768, playbackPercentage, unplayedCount, "jpg", "JPEG_MED"); AddImageResElement(item, writer, 1024, 768, playbackPercentage, unplayedCount, "jpg", "JPEG_MED");
AddImageResElement(item, element, 640, 480, playbackPercentage, unplayedCount, "jpg", "JPEG_SM"); AddImageResElement(item, writer, 640, 480, playbackPercentage, unplayedCount, "jpg", "JPEG_SM");
AddImageResElement(item, element, 4096, 4096, playbackPercentage, unplayedCount, "png", "PNG_LRG"); AddImageResElement(item, writer, 4096, 4096, playbackPercentage, unplayedCount, "png", "PNG_LRG");
AddImageResElement(item, element, 160, 160, playbackPercentage, unplayedCount, "png", "PNG_TN"); AddImageResElement(item, writer, 160, 160, playbackPercentage, unplayedCount, "png", "PNG_TN");
} }
} }
private void AddEmbeddedImageAsCover(string name, XmlElement element) private void AddEmbeddedImageAsCover(string name, XmlWriter writer)
{ {
var result = element.OwnerDocument; writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP);
writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn);
writer.WriteString(_serverAddress + "/Dlna/icons/people480.jpg");
writer.WriteEndElement();
var icon = result.CreateElement("upnp", "albumArtURI", NS_UPNP); writer.WriteElementString("upnp", "icon", NS_UPNP, _serverAddress + "/Dlna/icons/people48.jpg");
var profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
profile.InnerText = _profile.AlbumArtPn;
icon.SetAttributeNode(profile);
icon.InnerText = _serverAddress + "/Dlna/icons/people480.jpg";
element.AppendChild(icon);
icon = result.CreateElement("upnp", "icon", NS_UPNP);
icon.InnerText = _serverAddress + "/Dlna/icons/people48.jpg";
element.AppendChild(icon);
} }
private void AddImageResElement(BaseItem item, private void AddImageResElement(BaseItem item,
XmlElement element, XmlWriter writer,
int maxWidth, int maxWidth,
int maxHeight, int maxHeight,
int playbackPercentage, int playbackPercentage,
@ -960,13 +953,9 @@ namespace Emby.Dlna.Didl
return; return;
} }
var result = element.OwnerDocument;
var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, playbackPercentage, unplayedCount, format); var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, playbackPercentage, unplayedCount, format);
var res = result.CreateElement(string.Empty, "res", NS_DIDL); writer.WriteStartElement(string.Empty, "res", NS_DIDL);
res.InnerText = albumartUrlInfo.Url;
var width = albumartUrlInfo.Width; var width = albumartUrlInfo.Width;
var height = albumartUrlInfo.Height; var height = albumartUrlInfo.Height;
@ -974,7 +963,7 @@ namespace Emby.Dlna.Didl
var contentFeatures = new ContentFeatureBuilder(_profile) var contentFeatures = new ContentFeatureBuilder(_profile)
.BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn); .BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn);
res.SetAttribute("protocolInfo", String.Format( writer.WriteAttributeString("protocolInfo", String.Format(
"http-get:*:{0}:{1}", "http-get:*:{0}:{1}",
MimeTypes.GetMimeType("file." + format), MimeTypes.GetMimeType("file." + format),
contentFeatures contentFeatures
@ -982,10 +971,12 @@ namespace Emby.Dlna.Didl
if (width.HasValue && height.HasValue) if (width.HasValue && height.HasValue)
{ {
res.SetAttribute("resolution", string.Format("{0}x{1}", width.Value, height.Value)); writer.WriteAttributeString("resolution", string.Format("{0}x{1}", width.Value, height.Value));
} }
element.AppendChild(res); writer.WriteString(albumartUrlInfo.Url);
writer.WriteEndElement();
} }
private ImageDownloadInfo GetImageInfo(BaseItem item) private ImageDownloadInfo GetImageInfo(BaseItem item)

View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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
{
get { return (null == _encoding) ? base.Encoding : _encoding; }
}
}
}

View File

@ -16,12 +16,8 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
#if NETSTANDARD1_6 using MediaBrowser.Model.Reflection;
using System.Reflection;
#endif
namespace Emby.Dlna namespace Emby.Dlna
{ {
@ -33,6 +29,7 @@ namespace Emby.Dlna
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly IAssemblyInfo _assemblyInfo;
private readonly Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>> _profiles = new Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>>(StringComparer.Ordinal); private readonly Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>> _profiles = new Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>>(StringComparer.Ordinal);
@ -40,7 +37,7 @@ namespace Emby.Dlna
IFileSystem fileSystem, IFileSystem fileSystem,
IApplicationPaths appPaths, IApplicationPaths appPaths,
ILogger logger, ILogger logger,
IJsonSerializer jsonSerializer, IServerApplicationHost appHost) IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IAssemblyInfo assemblyInfo)
{ {
_xmlSerializer = xmlSerializer; _xmlSerializer = xmlSerializer;
_fileSystem = fileSystem; _fileSystem = fileSystem;
@ -48,6 +45,7 @@ namespace Emby.Dlna
_logger = logger; _logger = logger;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_appHost = appHost; _appHost = appHost;
_assemblyInfo = assemblyInfo;
} }
public void InitProfiles() public void InitProfiles()
@ -306,7 +304,7 @@ namespace Emby.Dlna
.Where(i => i != null) .Where(i => i != null)
.ToList(); .ToList();
} }
catch (DirectoryNotFoundException) catch (IOException)
{ {
return new List<DeviceProfile>(); return new List<DeviceProfile>();
} }
@ -400,17 +398,11 @@ namespace Emby.Dlna
private void ExtractSystemProfiles() private void ExtractSystemProfiles()
{ {
#if NET46
var assembly = GetType().Assembly;
var namespaceName = GetType().Namespace + ".Profiles.Json."; var namespaceName = GetType().Namespace + ".Profiles.Json.";
#elif NETSTANDARD1_6
var assembly = GetType().GetTypeInfo().Assembly;
var namespaceName = GetType().GetTypeInfo().Namespace + ".Profiles.Json.";
#endif
var systemProfilesPath = SystemProfilesPath; var systemProfilesPath = SystemProfilesPath;
foreach (var name in assembly.GetManifestResourceNames() foreach (var name in _assemblyInfo.GetManifestResourceNames(GetType())
.Where(i => i.StartsWith(namespaceName)) .Where(i => i.StartsWith(namespaceName))
.ToList()) .ToList())
{ {
@ -418,9 +410,9 @@ namespace Emby.Dlna
var path = Path.Combine(systemProfilesPath, filename); var path = Path.Combine(systemProfilesPath, filename);
using (var stream = assembly.GetManifestResourceStream(name)) using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), name))
{ {
var fileInfo = new FileInfo(path); var fileInfo = _fileSystem.GetFileInfo(path);
if (!fileInfo.Exists || fileInfo.Length != stream.Length) if (!fileInfo.Exists || fileInfo.Length != stream.Length)
{ {
@ -512,7 +504,7 @@ namespace Emby.Dlna
try try
{ {
File.Delete(Path.ChangeExtension(path, ".xml")); _fileSystem.DeleteFile(Path.ChangeExtension(path, ".xml"));
} }
catch catch
{ {
@ -562,20 +554,11 @@ namespace Emby.Dlna
var resource = GetType().Namespace + ".Images." + filename.ToLower(); var resource = GetType().Namespace + ".Images." + filename.ToLower();
#if NET46
return new ImageStream return new ImageStream
{ {
Format = format, Format = format,
Stream = GetType().Assembly.GetManifestResourceStream(resource) Stream = _assemblyInfo.GetManifestResourceStream(GetType(), resource)
}; };
#elif NETSTANDARD1_6
return new ImageStream
{
Format = format,
Stream = GetType().GetTypeInfo().Assembly.GetManifestResourceStream(resource)
};
#endif
throw new NotImplementedException();
} }
} }

202
Emby.Dlna/Emby.Dlna.csproj Normal file
View File

@ -0,0 +1,202 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{805844AB-E92F-45E6-9D99-4F6D48D129A5}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Emby.Dlna</RootNamespace>
<AssemblyName>Emby.Dlna</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
<EmbeddedResource Include="Profiles\Json\BubbleUPnp.json" />
<EmbeddedResource Include="Profiles\Json\Default.json" />
<EmbeddedResource Include="Profiles\Json\Denon AVR.json" />
<EmbeddedResource Include="Profiles\Json\DirecTV HD-DVR.json" />
<EmbeddedResource Include="Profiles\Json\Dish Hopper-Joey.json" />
<EmbeddedResource Include="Profiles\Json\foobar2000.json" />
<EmbeddedResource Include="Profiles\Json\Kodi.json" />
<EmbeddedResource Include="Profiles\Json\LG Smart TV.json" />
<EmbeddedResource Include="Profiles\Json\Linksys DMA2100.json" />
<EmbeddedResource Include="Profiles\Json\MediaMonkey.json" />
<EmbeddedResource Include="Profiles\Json\Panasonic Viera.json" />
<EmbeddedResource Include="Profiles\Json\Popcorn Hour.json" />
<EmbeddedResource Include="Profiles\Json\Samsung Smart TV.json" />
<EmbeddedResource Include="Profiles\Json\Sony Blu-ray Player 2013.json" />
<EmbeddedResource Include="Profiles\Json\Sony Blu-ray Player 2014.json" />
<EmbeddedResource Include="Profiles\Json\Sony Blu-ray Player 2015.json" />
<EmbeddedResource Include="Profiles\Json\Sony Blu-ray Player 2016.json" />
<EmbeddedResource Include="Profiles\Json\Sony Blu-ray Player.json" />
<EmbeddedResource Include="Profiles\Json\Sony Bravia %282010%29.json" />
<EmbeddedResource Include="Profiles\Json\Sony Bravia %282011%29.json" />
<EmbeddedResource Include="Profiles\Json\Sony Bravia %282012%29.json" />
<EmbeddedResource Include="Profiles\Json\Sony Bravia %282013%29.json" />
<EmbeddedResource Include="Profiles\Json\Sony Bravia %282014%29.json" />
<EmbeddedResource Include="Profiles\Json\Sony PlayStation 3.json" />
<EmbeddedResource Include="Profiles\Json\Sony PlayStation 4.json" />
<EmbeddedResource Include="Profiles\Json\Vlc.json" />
<EmbeddedResource Include="Profiles\Json\WDTV Live.json" />
<EmbeddedResource Include="Profiles\Json\Xbox 360.json" />
<EmbeddedResource Include="Profiles\Json\Xbox One.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="Common\Argument.cs" />
<Compile Include="Common\DeviceIcon.cs" />
<Compile Include="Common\DeviceService.cs" />
<Compile Include="Common\ServiceAction.cs" />
<Compile Include="Common\StateVariable.cs" />
<Compile Include="ConfigurationExtension.cs" />
<Compile Include="ConnectionManager\ConnectionManager.cs" />
<Compile Include="ConnectionManager\ConnectionManagerXmlBuilder.cs" />
<Compile Include="ConnectionManager\ControlHandler.cs" />
<Compile Include="ConnectionManager\ServiceActionListBuilder.cs" />
<Compile Include="ContentDirectory\ContentDirectory.cs" />
<Compile Include="ContentDirectory\ContentDirectoryBrowser.cs" />
<Compile Include="ContentDirectory\ContentDirectoryXmlBuilder.cs" />
<Compile Include="ContentDirectory\ControlHandler.cs" />
<Compile Include="ContentDirectory\ServiceActionListBuilder.cs" />
<Compile Include="Didl\DidlBuilder.cs" />
<Compile Include="Didl\Filter.cs" />
<Compile Include="Didl\StringWriterWithEncoding.cs" />
<Compile Include="DlnaManager.cs" />
<Compile Include="Eventing\EventManager.cs" />
<Compile Include="Eventing\EventSubscription.cs" />
<Compile Include="Main\DlnaEntryPoint.cs" />
<Compile Include="MediaReceiverRegistrar\ControlHandler.cs" />
<Compile Include="MediaReceiverRegistrar\MediaReceiverRegistrar.cs" />
<Compile Include="MediaReceiverRegistrar\MediaReceiverRegistrarXmlBuilder.cs" />
<Compile Include="MediaReceiverRegistrar\ServiceActionListBuilder.cs" />
<Compile Include="PlayTo\CurrentIdEventArgs.cs" />
<Compile Include="PlayTo\Device.cs" />
<Compile Include="PlayTo\DeviceInfo.cs" />
<Compile Include="PlayTo\PlaybackProgressEventArgs.cs" />
<Compile Include="PlayTo\PlaybackStartEventArgs.cs" />
<Compile Include="PlayTo\PlaybackStoppedEventArgs.cs" />
<Compile Include="PlayTo\PlaylistItem.cs" />
<Compile Include="PlayTo\PlaylistItemFactory.cs" />
<Compile Include="PlayTo\PlayToController.cs" />
<Compile Include="PlayTo\PlayToManager.cs" />
<Compile Include="PlayTo\SsdpHttpClient.cs" />
<Compile Include="PlayTo\TransportCommands.cs" />
<Compile Include="PlayTo\TRANSPORTSTATE.cs" />
<Compile Include="PlayTo\TransportStateEventArgs.cs" />
<Compile Include="PlayTo\uBaseObject.cs" />
<Compile Include="PlayTo\uParser.cs" />
<Compile Include="PlayTo\uParserObject.cs" />
<Compile Include="PlayTo\UpnpContainer.cs" />
<Compile Include="PlayTo\uPnpNamespaces.cs" />
<Compile Include="ProfileSerialization\CodecProfile.cs" />
<Compile Include="ProfileSerialization\ContainerProfile.cs" />
<Compile Include="ProfileSerialization\DeviceProfile.cs" />
<Compile Include="ProfileSerialization\DirectPlayProfile.cs" />
<Compile Include="ProfileSerialization\HttpHeaderInfo.cs" />
<Compile Include="ProfileSerialization\ProfileCondition.cs" />
<Compile Include="ProfileSerialization\ResponseProfile.cs" />
<Compile Include="ProfileSerialization\SubtitleProfile.cs" />
<Compile Include="ProfileSerialization\TranscodingProfile.cs" />
<Compile Include="ProfileSerialization\XmlAttribute.cs" />
<Compile Include="Profiles\BubbleUpnpProfile.cs" />
<Compile Include="Profiles\DefaultProfile.cs" />
<Compile Include="Profiles\DenonAvrProfile.cs" />
<Compile Include="Profiles\DirectTvProfile.cs" />
<Compile Include="Profiles\DishHopperJoeyProfile.cs" />
<Compile Include="Profiles\Foobar2000Profile.cs" />
<Compile Include="Profiles\KodiProfile.cs" />
<Compile Include="Profiles\LgTvProfile.cs" />
<Compile Include="Profiles\LinksysDMA2100Profile.cs" />
<Compile Include="Profiles\MediaMonkeyProfile.cs" />
<Compile Include="Profiles\PanasonicVieraProfile.cs" />
<Compile Include="Profiles\PopcornHourProfile.cs" />
<Compile Include="Profiles\SamsungSmartTvProfile.cs" />
<Compile Include="Profiles\SonyBlurayPlayer2013.cs" />
<Compile Include="Profiles\SonyBlurayPlayer2014.cs" />
<Compile Include="Profiles\SonyBlurayPlayer2015.cs" />
<Compile Include="Profiles\SonyBlurayPlayer2016.cs" />
<Compile Include="Profiles\SonyBlurayPlayerProfile.cs" />
<Compile Include="Profiles\SonyBravia2010Profile.cs" />
<Compile Include="Profiles\SonyBravia2011Profile.cs" />
<Compile Include="Profiles\SonyBravia2012Profile.cs" />
<Compile Include="Profiles\SonyBravia2013Profile.cs" />
<Compile Include="Profiles\SonyBravia2014Profile.cs" />
<Compile Include="Profiles\SonyPs3Profile.cs" />
<Compile Include="Profiles\SonyPs4Profile.cs" />
<Compile Include="Profiles\VlcProfile.cs" />
<Compile Include="Profiles\WdtvLiveProfile.cs" />
<Compile Include="Profiles\Xbox360Profile.cs" />
<Compile Include="Profiles\XboxOneProfile.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Server\DescriptionXmlBuilder.cs" />
<Compile Include="Server\Headers.cs" />
<Compile Include="Server\UpnpDevice.cs" />
<Compile Include="Service\BaseControlHandler.cs" />
<Compile Include="Service\BaseService.cs" />
<Compile Include="Service\ControlErrorHandler.cs" />
<Compile Include="Service\ServiceXmlBuilder.cs" />
<Compile Include="Ssdp\DeviceDiscovery.cs" />
<Compile Include="Ssdp\Extensions.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Images\logo120.jpg" />
<EmbeddedResource Include="Images\logo120.png" />
<EmbeddedResource Include="Images\logo240.jpg" />
<EmbeddedResource Include="Images\logo240.png" />
<EmbeddedResource Include="Images\logo48.jpg" />
<EmbeddedResource Include="Images\logo48.png" />
<EmbeddedResource Include="Images\people48.jpg" />
<EmbeddedResource Include="Images\people48.png" />
<EmbeddedResource Include="Images\people480.jpg" />
<EmbeddedResource Include="Images\people480.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Name>MediaBrowser.Common</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
<ProjectReference Include="..\RSSDP\RSSDP.csproj">
<Project>{21002819-c39a-4d3e-be83-2a276a77fb1f}</Project>
<Name>RSSDP</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.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.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>f40e364d-01d9-4bbf-b82c-5d6c55e0a1f5</ProjectGuid>
<RootNamespace>Emby.Dlna</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -19,6 +19,8 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Threading;
using Rssdp; using Rssdp;
using Rssdp.Infrastructure; using Rssdp.Infrastructure;
@ -29,7 +31,6 @@ namespace Emby.Dlna.Main
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly INetworkManager _network;
private PlayToManager _manager; private PlayToManager _manager;
private readonly ISessionManager _sessionManager; private readonly ISessionManager _sessionManager;
@ -49,10 +50,13 @@ namespace Emby.Dlna.Main
private bool _dlnaServerStarted; private bool _dlnaServerStarted;
private SsdpDevicePublisher _Publisher; private SsdpDevicePublisher _Publisher;
private readonly ITimerFactory _timerFactory;
private readonly ISocketFactory _socketFactory;
public DlnaEntryPoint(IServerConfigurationManager config, public DlnaEntryPoint(IServerConfigurationManager config,
ILogManager logManager, ILogManager logManager,
IServerApplicationHost appHost, IServerApplicationHost appHost,
INetworkManager network,
ISessionManager sessionManager, ISessionManager sessionManager,
IHttpClient httpClient, IHttpClient httpClient,
ILibraryManager libraryManager, ILibraryManager libraryManager,
@ -62,11 +66,10 @@ namespace Emby.Dlna.Main
IUserDataManager userDataManager, IUserDataManager userDataManager,
ILocalizationManager localization, ILocalizationManager localization,
IMediaSourceManager mediaSourceManager, IMediaSourceManager mediaSourceManager,
IDeviceDiscovery deviceDiscovery, IMediaEncoder mediaEncoder) IDeviceDiscovery deviceDiscovery, IMediaEncoder mediaEncoder, ISocketFactory socketFactory, ITimerFactory timerFactory)
{ {
_config = config; _config = config;
_appHost = appHost; _appHost = appHost;
_network = network;
_sessionManager = sessionManager; _sessionManager = sessionManager;
_httpClient = httpClient; _httpClient = httpClient;
_libraryManager = libraryManager; _libraryManager = libraryManager;
@ -78,6 +81,8 @@ namespace Emby.Dlna.Main
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_deviceDiscovery = deviceDiscovery; _deviceDiscovery = deviceDiscovery;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_socketFactory = socketFactory;
_timerFactory = timerFactory;
_logger = logManager.GetLogger("Dlna"); _logger = logManager.GetLogger("Dlna");
} }
@ -164,7 +169,7 @@ namespace Emby.Dlna.Main
private void StartPublishing() private void StartPublishing()
{ {
SsdpDevicePublisherBase.LogFunction = LogMessage; SsdpDevicePublisherBase.LogFunction = LogMessage;
_Publisher = new SsdpDevicePublisher(); _Publisher = new SsdpDevicePublisher(_socketFactory, _timerFactory, "Windows", "10");
} }
private void StartDeviceDiscovery() private void StartDeviceDiscovery()
@ -328,7 +333,8 @@ namespace Emby.Dlna.Main
_userDataManager, _userDataManager,
_localization, _localization,
_mediaSourceManager, _mediaSourceManager,
_mediaEncoder); _mediaEncoder,
_timerFactory);
_manager.Start(); _manager.Start();
} }

View File

@ -5,15 +5,12 @@ using Emby.Dlna.Service;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using MediaBrowser.Model.Xml;
namespace Emby.Dlna.MediaReceiverRegistrar namespace Emby.Dlna.MediaReceiverRegistrar
{ {
public class ControlHandler : BaseControlHandler public class ControlHandler : BaseControlHandler
{ {
public ControlHandler(IServerConfigurationManager config, ILogger logger) : base(config, logger)
{
}
protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams) protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
{ {
if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase))
@ -39,5 +36,9 @@ namespace Emby.Dlna.MediaReceiverRegistrar
{ "Result", "1" } { "Result", "1" }
}; };
} }
public ControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(config, logger, xmlReaderSettingsFactory)
{
}
} }
} }

View File

@ -5,17 +5,20 @@ using Emby.Dlna.Service;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using MediaBrowser.Model.Xml;
namespace Emby.Dlna.MediaReceiverRegistrar namespace Emby.Dlna.MediaReceiverRegistrar
{ {
public class MediaReceiverRegistrar : BaseService, IMediaReceiverRegistrar, IDisposable public class MediaReceiverRegistrar : BaseService, IMediaReceiverRegistrar, IDisposable
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config) public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(logger, httpClient) : base(logger, httpClient)
{ {
_config = config; _config = config;
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
} }
public string GetServiceXml(IDictionary<string, string> headers) public string GetServiceXml(IDictionary<string, string> headers)
@ -27,7 +30,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar
{ {
return new ControlHandler( return new ControlHandler(
_config, _config,
Logger) Logger, XmlReaderSettingsFactory)
.ProcessControlRequest(request); .ProcessControlRequest(request);
} }

View File

@ -14,6 +14,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq; using System.Xml.Linq;
using Emby.Dlna.Server; using Emby.Dlna.Server;
using MediaBrowser.Model.Threading;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo
{ {
@ -24,7 +25,7 @@ namespace Emby.Dlna.PlayTo
#region Fields & Properties #region Fields & Properties
private Timer _timer; private ITimer _timer;
public DeviceInfo Properties { get; set; } public DeviceInfo Properties { get; set; }
@ -96,12 +97,15 @@ namespace Emby.Dlna.PlayTo
public DateTime DateLastActivity { get; private set; } public DateTime DateLastActivity { get; private set; }
public Action OnDeviceUnavailable { get; set; } public Action OnDeviceUnavailable { get; set; }
public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config) private readonly ITimerFactory _timerFactory;
public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config, ITimerFactory timerFactory)
{ {
Properties = deviceProperties; Properties = deviceProperties;
_httpClient = httpClient; _httpClient = httpClient;
_logger = logger; _logger = logger;
_config = config; _config = config;
_timerFactory = timerFactory;
} }
private int GetPlaybackTimerIntervalMs() private int GetPlaybackTimerIntervalMs()
@ -116,7 +120,7 @@ namespace Emby.Dlna.PlayTo
public void Start() public void Start()
{ {
_timer = new Timer(TimerCallback, null, GetPlaybackTimerIntervalMs(), GetInactiveTimerIntervalMs()); _timer = _timerFactory.Create(TimerCallback, null, GetPlaybackTimerIntervalMs(), GetInactiveTimerIntervalMs());
_timerActive = false; _timerActive = false;
} }
@ -830,7 +834,7 @@ namespace Emby.Dlna.PlayTo
set; set;
} }
public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger) public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, ITimerFactory timerFactory)
{ {
var ssdpHttpClient = new SsdpHttpClient(httpClient, config); var ssdpHttpClient = new SsdpHttpClient(httpClient, config);
@ -922,7 +926,7 @@ namespace Emby.Dlna.PlayTo
} }
} }
var device = new Device(deviceProperties, httpClient, logger, config); var device = new Device(deviceProperties, httpClient, logger, config, timerFactory);
if (isRenderer) if (isRenderer)
{ {

View File

@ -11,12 +11,12 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Threading;
namespace Emby.Dlna.PlayTo namespace Emby.Dlna.PlayTo
{ {
@ -38,11 +38,12 @@ namespace Emby.Dlna.PlayTo
private readonly IDeviceDiscovery _deviceDiscovery; private readonly IDeviceDiscovery _deviceDiscovery;
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private readonly ITimerFactory _timerFactory;
private readonly List<string> _nonRendererUrls = new List<string>(); private readonly List<string> _nonRendererUrls = new List<string>();
private DateTime _lastRendererClear; private DateTime _lastRendererClear;
public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder) public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, ITimerFactory timerFactory)
{ {
_logger = logger; _logger = logger;
_sessionManager = sessionManager; _sessionManager = sessionManager;
@ -58,6 +59,7 @@ namespace Emby.Dlna.PlayTo
_localization = localization; _localization = localization;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_timerFactory = timerFactory;
} }
public void Start() public void Start()
@ -107,7 +109,7 @@ namespace Emby.Dlna.PlayTo
var uri = info.Location; var uri = info.Location;
_logger.Debug("Attempting to create PlayToController from location {0}", location); _logger.Debug("Attempting to create PlayToController from location {0}", location);
var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger).ConfigureAwait(false); var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, _timerFactory).ConfigureAwait(false);
if (device.RendererCommands == null) if (device.RendererCommands == null)
{ {

View File

@ -1,19 +1,30 @@
using System.Reflection; using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle("Emby.Dlna2")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Emby.Dlna")] [assembly: AssemblyProduct("Emby.Dlna2")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
// Setting ComVisible to false makes the types in this assembly not visible // Version information for an assembly consists of the following four values:
// to COM components. If you need to access a type in this assembly from //
// COM, set the ComVisible attribute to true on that type. // Major Version
[assembly: ComVisible(false)] // Minor Version
// Build Number
// The following GUID is for the ID of the typelib if this project is exposed to COM // Revision
[assembly: Guid("f40e364d-01d9-4bbf-b82c-5d6c55e0a1f5")] //
// 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.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -216,7 +216,17 @@ namespace Emby.Dlna.Server
return "Emby - " + _serverName; return "Emby - " + _serverName;
} }
var characters = _serverName.Where(c => (char.IsLetterOrDigit(c) || c == '-')).ToArray(); var characterList = new List<char>();
foreach (var c in _serverName)
{
if (char.IsLetterOrDigit(c) || c == '-')
{
characterList.Add(c);
}
}
var characters = characterList.ToArray();
var serverName = new string(characters); var serverName = new string(characters);

View File

@ -11,7 +11,7 @@ namespace Emby.Dlna.Server
{ {
private readonly bool _asIs = false; private readonly bool _asIs = false;
private readonly Dictionary<string, string> _dict = new Dictionary<string, string>(); private readonly Dictionary<string, string> _dict = new Dictionary<string, string>();
private readonly static Regex Validator = new Regex(@"^[a-z\d][a-z\d_.-]+$", RegexOptions.Compiled | RegexOptions.IgnoreCase); private readonly static Regex Validator = new Regex(@"^[a-z\d][a-z\d_.-]+$", RegexOptions.IgnoreCase);
public Headers(bool asIs) public Headers(bool asIs)
{ {
@ -42,13 +42,6 @@ namespace Emby.Dlna.Server
return hb.ToString(); return hb.ToString();
} }
} }
public Stream HeaderStream
{
get
{
return new MemoryStream(Encoding.ASCII.GetBytes(HeaderBlock));
}
}
public bool IsReadOnly public bool IsReadOnly
{ {
get get

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Net; using System.Net;
using MediaBrowser.Model.Net;
namespace Emby.Dlna.Server namespace Emby.Dlna.Server
{ {
@ -9,9 +10,9 @@ namespace Emby.Dlna.Server
public readonly string Type; public readonly string Type;
public readonly string USN; public readonly string USN;
public readonly string Uuid; public readonly string Uuid;
public readonly IPAddress Address; public readonly IpAddressInfo Address;
public UpnpDevice(string aUuid, string aType, Uri aDescriptor, IPAddress address) public UpnpDevice(string aUuid, string aType, Uri aDescriptor, IpAddressInfo address)
{ {
Uuid = aUuid; Uuid = aUuid;
Type = aType; Type = aType;

View File

@ -4,9 +4,12 @@ using Emby.Dlna.Server;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using Emby.Dlna.Didl;
using MediaBrowser.Model.Xml;
namespace Emby.Dlna.Service namespace Emby.Dlna.Service
{ {
@ -16,11 +19,13 @@ namespace Emby.Dlna.Service
protected readonly IServerConfigurationManager Config; protected readonly IServerConfigurationManager Config;
protected readonly ILogger Logger; protected readonly ILogger Logger;
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
protected BaseControlHandler(IServerConfigurationManager config, ILogger logger) protected BaseControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
{ {
Config = config; Config = config;
Logger = logger; Logger = logger;
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
} }
public ControlResponse ProcessControlRequest(ControlRequest request) public ControlResponse ProcessControlRequest(ControlRequest request)
@ -53,47 +58,57 @@ namespace Emby.Dlna.Service
private ControlResponse ProcessControlRequestInternal(ControlRequest request) private ControlResponse ProcessControlRequestInternal(ControlRequest request)
{ {
var soap = new XmlDocument(); ControlRequestInfo requestInfo = null;
soap.LoadXml(request.InputXml);
var sparams = new Headers();
var body = soap.GetElementsByTagName("Body", NS_SOAPENV).Item(0);
var method = body.FirstChild; using (var streamReader = new StreamReader(request.InputXml))
foreach (var p in method.ChildNodes)
{ {
var e = p as XmlElement; var readerSettings = XmlReaderSettingsFactory.Create(false);
if (e == null)
readerSettings.CheckCharacters = false;
readerSettings.IgnoreProcessingInstructions = true;
readerSettings.IgnoreComments = true;
using (var reader = XmlReader.Create(streamReader, readerSettings))
{ {
continue; requestInfo = ParseRequest(reader);
} }
sparams.Add(e.LocalName, e.InnerText.Trim());
} }
Logger.Debug("Received control request {0}", method.LocalName); Logger.Debug("Received control request {0}", requestInfo.LocalName);
var result = GetResult(method.LocalName, sparams); var result = GetResult(requestInfo.LocalName, requestInfo.Headers);
var env = new XmlDocument(); var settings = new XmlWriterSettings
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); Encoding = Encoding.UTF8,
ri.InnerText = i.Value; CloseOutput = false
response.AppendChild(ri); };
StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
writer.WriteStartDocument(true);
writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV);
writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
foreach (var i in result)
{
writer.WriteStartElement(i.Key);
writer.WriteString(i.Value);
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
} }
var xml = env.OuterXml.Replace("xmlns:m=", "xmlns:u="); var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=");
var controlResponse = new ControlResponse var controlResponse = new ControlResponse
{ {
@ -108,6 +123,102 @@ namespace Emby.Dlna.Service
return controlResponse; return controlResponse;
} }
private ControlRequestInfo ParseRequest(XmlReader reader)
{
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF)
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.LocalName)
{
case "Body":
{
using (var subReader = reader.ReadSubtree())
{
return ParseBodyTag(subReader);
}
}
default:
{
reader.Skip();
break;
}
}
}
else
{
reader.Read();
}
}
return new ControlRequestInfo();
}
private ControlRequestInfo ParseBodyTag(XmlReader reader)
{
var result = new ControlRequestInfo();
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF)
{
if (reader.NodeType == XmlNodeType.Element)
{
result.LocalName = reader.LocalName;
result.NamespaceURI = reader.NamespaceURI;
using (var subReader = reader.ReadSubtree())
{
result.Headers = ParseFirstBodyChild(subReader);
return result;
}
}
else
{
reader.Read();
}
}
return result;
}
private Headers ParseFirstBodyChild(XmlReader reader)
{
var result = new Headers();
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF)
{
if (reader.NodeType == XmlNodeType.Element)
{
result.Add(reader.LocalName, reader.ReadElementContentAsString());
}
else
{
reader.Read();
}
}
return result;
}
private class ControlRequestInfo
{
public string LocalName;
public string NamespaceURI;
public Headers Headers = new Headers();
}
protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams); protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams);
private void LogRequest(ControlRequest request) private void LogRequest(ControlRequest request)

View File

@ -1,6 +1,9 @@
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using System; using System;
using System.IO;
using System.Text;
using System.Xml; using System.Xml;
using Emby.Dlna.Didl;
namespace Emby.Dlna.Service namespace Emby.Dlna.Service
{ {
@ -10,30 +13,41 @@ namespace Emby.Dlna.Service
public ControlResponse GetResponse(Exception ex) public ControlResponse GetResponse(Exception ex)
{ {
var env = new XmlDocument(); var settings = new XmlWriterSettings
env.AppendChild(env.CreateXmlDeclaration("1.0", "utf-8", "yes")); {
var envelope = env.CreateElement("SOAP-ENV", "Envelope", NS_SOAPENV); Encoding = Encoding.UTF8,
env.AppendChild(envelope); CloseOutput = false
envelope.SetAttribute("encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/"); };
var rbody = env.CreateElement("SOAP-ENV:Body", NS_SOAPENV); StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
env.DocumentElement.AppendChild(rbody);
var fault = env.CreateElement("SOAP-ENV", "Fault", NS_SOAPENV); using (XmlWriter writer = XmlWriter.Create(builder, settings))
var faultCode = env.CreateElement("faultcode"); {
faultCode.InnerText = "500"; writer.WriteStartDocument(true);
fault.AppendChild(faultCode);
var faultString = env.CreateElement("faultstring"); writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV);
faultString.InnerText = ex.ToString(); writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
fault.AppendChild(faultString);
var detail = env.CreateDocumentFragment(); writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
detail.InnerXml = "<detail><UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\"><errorCode>401</errorCode><errorDescription>Invalid Action</errorDescription></UPnPError></detail>"; writer.WriteStartElement("SOAP-ENV", "Fault", NS_SOAPENV);
fault.AppendChild(detail);
rbody.AppendChild(fault); writer.WriteElementString("faultcode", "500");
writer.WriteElementString("faultstring", ex.Message);
writer.WriteStartElement("detail");
writer.WriteRaw("<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\"><errorCode>401</errorCode><errorDescription>Invalid Action</errorDescription></UPnPError>");
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
}
return new ControlResponse return new ControlResponse
{ {
Xml = env.OuterXml, Xml = builder.ToString(),
IsSuccessful = false IsSuccessful = false
}; };
} }

View File

@ -7,12 +7,13 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Threading;
using Rssdp; using Rssdp;
namespace Emby.Dlna.Ssdp namespace Emby.Dlna.Ssdp
@ -30,18 +31,23 @@ namespace Emby.Dlna.Ssdp
private SsdpDeviceLocator _DeviceLocator; private SsdpDeviceLocator _DeviceLocator;
public DeviceDiscovery(ILogger logger, IServerConfigurationManager config) private readonly ITimerFactory _timerFactory;
private readonly ISocketFactory _socketFactory;
public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, ISocketFactory socketFactory, ITimerFactory timerFactory)
{ {
_tokenSource = new CancellationTokenSource(); _tokenSource = new CancellationTokenSource();
_logger = logger; _logger = logger;
_config = config; _config = config;
_socketFactory = socketFactory;
_timerFactory = timerFactory;
} }
// Call this method from somewhere in your code to start the search. // Call this method from somewhere in your code to start the search.
public void BeginSearch() public void BeginSearch()
{ {
_DeviceLocator = new SsdpDeviceLocator(); _DeviceLocator = new SsdpDeviceLocator(_socketFactory, _timerFactory);
// (Optional) Set the filter so we only see notifications for devices we care about // (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 // (can be any search target value i.e device type, uuid value etc - any value that appears in the

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq; using System.Xml.Linq;

View File

@ -1,57 +0,0 @@
{
"version": "1.0.0-*",
"dependencies": {
},
"frameworks": {
"net46": {
"frameworkAssemblies": {
"System.Collections": "4.0.0.0",
"System.IO": "4.0.0.0",
"System.Xml": "4.0.0.0",
"System.Xml.Linq": "4.0.0.0",
"System.Xml.Serialization": "4.0.0.0"
},
"dependencies": {
"MediaBrowser.Controller": {
"target": "project"
},
"MediaBrowser.Common": {
"target": "project"
},
"MediaBrowser.Model": {
"target": "project"
},
"RSSDP": {
"target": "project"
}
}
},
"netstandard1.6": {
"imports": "dnxcore50",
"dependencies": {
"NETStandard.Library": "1.6.0",
"MediaBrowser.Controller": {
"target": "project"
},
"MediaBrowser.Common": {
"target": "project"
},
"MediaBrowser.Model": {
"target": "project"
},
"System.Xml.XmlSerializer": "4.0.11",
"System.Xml.XDocument": "4.0.11",
"System.Xml.XmlDocument": "4.0.1",
"System.Reflection": "4.1.0",
"System.Reflection.Primitives": "4.0.1",
"System.Runtime.Loader": "4.0.0",
"RSSDP": {
"target": "project"
}
}
}
}
}

View File

@ -159,6 +159,9 @@
<Compile Include="ScheduledTasks\RefreshIntrosTask.cs" /> <Compile Include="ScheduledTasks\RefreshIntrosTask.cs" />
<Compile Include="ScheduledTasks\RefreshMediaLibraryTask.cs" /> <Compile Include="ScheduledTasks\RefreshMediaLibraryTask.cs" />
<Compile Include="ScheduledTasks\SystemUpdateTask.cs" /> <Compile Include="ScheduledTasks\SystemUpdateTask.cs" />
<Compile Include="Security\MBLicenseFile.cs" />
<Compile Include="Security\PluginSecurityManager.cs" />
<Compile Include="Security\RegRecord.cs" />
<Compile Include="ServerManager\ServerManager.cs" /> <Compile Include="ServerManager\ServerManager.cs" />
<Compile Include="ServerManager\WebSocketConnection.cs" /> <Compile Include="ServerManager\WebSocketConnection.cs" />
<Compile Include="Session\HttpSessionController.cs" /> <Compile Include="Session\HttpSessionController.cs" />

View File

@ -4,15 +4,18 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography;
using System.Text; using System.Text;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.IO;
namespace MediaBrowser.Server.Implementations.Security namespace Emby.Server.Implementations.Security
{ {
internal class MBLicenseFile internal class MBLicenseFile
{ {
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly ICryptographyProvider _cryptographyProvider;
public string RegKey public string RegKey
{ {
@ -40,9 +43,11 @@ namespace MediaBrowser.Server.Implementations.Security
private readonly object _fileLock = new object(); private readonly object _fileLock = new object();
private string _regKey; private string _regKey;
public MBLicenseFile(IApplicationPaths appPaths) public MBLicenseFile(IApplicationPaths appPaths, IFileSystem fileSystem, ICryptographyProvider cryptographyProvider)
{ {
_appPaths = appPaths; _appPaths = appPaths;
_fileSystem = fileSystem;
_cryptographyProvider = cryptographyProvider;
Load(); Load();
} }
@ -54,41 +59,30 @@ namespace MediaBrowser.Server.Implementations.Security
public void AddRegCheck(string featureId) public void AddRegCheck(string featureId)
{ {
using (var provider = new MD5CryptoServiceProvider()) var key = new Guid(_cryptographyProvider.GetMD5Bytes(Encoding.Unicode.GetBytes(featureId)));
{ var value = DateTime.UtcNow;
var key = new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(featureId)));
var value = DateTime.UtcNow;
SetUpdateRecord(key, value);
Save();
}
SetUpdateRecord(key, value);
Save();
} }
public void RemoveRegCheck(string featureId) public void RemoveRegCheck(string featureId)
{ {
using (var provider = new MD5CryptoServiceProvider()) var key = new Guid(_cryptographyProvider.GetMD5Bytes(Encoding.Unicode.GetBytes(featureId)));
{ DateTime val;
var key = new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(featureId)));
DateTime val;
_updateRecords.TryRemove(key, out val); _updateRecords.TryRemove(key, out val);
Save();
}
Save();
} }
public DateTime LastChecked(string featureId) public DateTime LastChecked(string featureId)
{ {
using (var provider = new MD5CryptoServiceProvider()) DateTime last;
{ _updateRecords.TryGetValue(new Guid(_cryptographyProvider.GetMD5Bytes(Encoding.Unicode.GetBytes(featureId))), out last);
DateTime last;
_updateRecords.TryGetValue(new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(featureId))), out last);
// guard agains people just putting a large number in the file // guard agains people just putting a large number in the file
return last < DateTime.UtcNow ? last : DateTime.MinValue; return last < DateTime.UtcNow ? last : DateTime.MinValue;
}
} }
private void Load() private void Load()
@ -99,15 +93,21 @@ namespace MediaBrowser.Server.Implementations.Security
{ {
try try
{ {
contents = File.ReadAllLines(licenseFile); contents = _fileSystem.ReadAllLines(licenseFile);
}
catch (DirectoryNotFoundException)
{
File.Create(licenseFile).Close();
} }
catch (FileNotFoundException) catch (FileNotFoundException)
{ {
File.Create(licenseFile).Close(); lock (_fileLock)
{
_fileSystem.WriteAllBytes(licenseFile, new byte[] {});
}
}
catch (IOException)
{
lock (_fileLock)
{
_fileSystem.WriteAllBytes(licenseFile, new byte[] { });
}
} }
} }
if (contents != null && contents.Length > 0) if (contents != null && contents.Length > 0)
@ -150,8 +150,11 @@ namespace MediaBrowser.Server.Implementations.Security
} }
var licenseFile = Filename; var licenseFile = Filename;
Directory.CreateDirectory(Path.GetDirectoryName(licenseFile)); _fileSystem.CreateDirectory(Path.GetDirectoryName(licenseFile));
lock (_fileLock) File.WriteAllLines(licenseFile, lines); lock (_fileLock)
{
_fileSystem.WriteAllLines(licenseFile, lines);
}
} }
} }
} }

View File

@ -5,18 +5,18 @@ using System.Linq;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Common.Security; using MediaBrowser.Common.Security;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Server.Implementations.Security namespace Emby.Server.Implementations.Security
{ {
/// <summary> /// <summary>
/// Class PluginSecurityManager /// Class PluginSecurityManager
@ -55,7 +55,7 @@ namespace MediaBrowser.Server.Implementations.Security
private MBLicenseFile _licenseFile; private MBLicenseFile _licenseFile;
private MBLicenseFile LicenseFile private MBLicenseFile LicenseFile
{ {
get { return _licenseFile ?? (_licenseFile = new MBLicenseFile(_appPaths)); } get { return _licenseFile ?? (_licenseFile = new MBLicenseFile(_appPaths, _fileSystem, _cryptographyProvider)); }
} }
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
@ -64,6 +64,7 @@ namespace MediaBrowser.Server.Implementations.Security
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ICryptographyProvider _cryptographyProvider;
private IEnumerable<IRequiresRegistration> _registeredEntities; private IEnumerable<IRequiresRegistration> _registeredEntities;
protected IEnumerable<IRequiresRegistration> RegisteredEntities protected IEnumerable<IRequiresRegistration> RegisteredEntities
@ -78,7 +79,7 @@ namespace MediaBrowser.Server.Implementations.Security
/// Initializes a new instance of the <see cref="PluginSecurityManager" /> class. /// Initializes a new instance of the <see cref="PluginSecurityManager" /> class.
/// </summary> /// </summary>
public PluginSecurityManager(IServerApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer, public PluginSecurityManager(IServerApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer,
IApplicationPaths appPaths, ILogManager logManager, IFileSystem fileSystem) IApplicationPaths appPaths, ILogManager logManager, IFileSystem fileSystem, ICryptographyProvider cryptographyProvider)
{ {
if (httpClient == null) if (httpClient == null)
{ {
@ -90,6 +91,7 @@ namespace MediaBrowser.Server.Implementations.Security
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_appPaths = appPaths; _appPaths = appPaths;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_cryptographyProvider = cryptographyProvider;
_logger = logManager.GetLogger("SecurityManager"); _logger = logManager.GetLogger("SecurityManager");
} }
@ -191,7 +193,7 @@ namespace MediaBrowser.Server.Implementations.Security
{ {
var msg = "Result from appstore registration was null."; var msg = "Result from appstore registration was null.";
_logger.Error(msg); _logger.Error(msg);
throw new ApplicationException(msg); throw new ArgumentException(msg);
} }
if (!String.IsNullOrEmpty(reg.key)) if (!String.IsNullOrEmpty(reg.key))
{ {
@ -200,7 +202,7 @@ namespace MediaBrowser.Server.Implementations.Security
} }
} }
catch (ApplicationException) catch (ArgumentException)
{ {
SaveAppStoreInfo(parameters); SaveAppStoreInfo(parameters);
throw; throw;
@ -213,14 +215,14 @@ namespace MediaBrowser.Server.Implementations.Security
{ {
throw new PaymentRequiredException(); throw new PaymentRequiredException();
} }
throw new ApplicationException("Error registering store sale"); throw new Exception("Error registering store sale");
} }
catch (Exception e) catch (Exception e)
{ {
_logger.ErrorException("Error registering appstore purchase {0}", e, parameters ?? "NO PARMS SENT"); _logger.ErrorException("Error registering appstore purchase {0}", e, parameters ?? "NO PARMS SENT");
SaveAppStoreInfo(parameters); SaveAppStoreInfo(parameters);
//TODO - could create a re-try routine on start-up if this file is there. For now we can handle manually. //TODO - could create a re-try routine on start-up if this file is there. For now we can handle manually.
throw new ApplicationException("Error registering store sale"); throw new Exception("Error registering store sale");
} }
} }

View File

@ -1,6 +1,6 @@
using System; using System;
namespace MediaBrowser.Server.Implementations.Security namespace Emby.Server.Implementations.Security
{ {
class RegRecord class RegRecord
{ {

View File

@ -152,41 +152,38 @@ namespace MediaBrowser.Api.Dlna
return ResultFactory.GetResult(xml, XMLContentType); return ResultFactory.GetResult(xml, XMLContentType);
} }
public async Task<object> Post(ProcessMediaReceiverRegistrarControlRequest request) public object Post(ProcessMediaReceiverRegistrarControlRequest request)
{ {
var response = await PostAsync(request.RequestStream, _mediaReceiverRegistrar).ConfigureAwait(false); var response = PostAsync(request.RequestStream, _mediaReceiverRegistrar);
return ResultFactory.GetResult(response.Xml, XMLContentType); return ResultFactory.GetResult(response.Xml, XMLContentType);
} }
public async Task<object> Post(ProcessContentDirectoryControlRequest request) public object Post(ProcessContentDirectoryControlRequest request)
{ {
var response = await PostAsync(request.RequestStream, _contentDirectory).ConfigureAwait(false); var response = PostAsync(request.RequestStream, _contentDirectory);
return ResultFactory.GetResult(response.Xml, XMLContentType); return ResultFactory.GetResult(response.Xml, XMLContentType);
} }
public async Task<object> Post(ProcessConnectionManagerControlRequest request) public object Post(ProcessConnectionManagerControlRequest request)
{ {
var response = await PostAsync(request.RequestStream, _connectionManager).ConfigureAwait(false); var response = PostAsync(request.RequestStream, _connectionManager);
return ResultFactory.GetResult(response.Xml, XMLContentType); return ResultFactory.GetResult(response.Xml, XMLContentType);
} }
private async Task<ControlResponse> PostAsync(Stream requestStream, IUpnpService service) private ControlResponse PostAsync(Stream requestStream, IUpnpService service)
{ {
var id = GetPathValue(2); var id = GetPathValue(2);
using (var reader = new StreamReader(requestStream)) return service.ProcessControlRequest(new ControlRequest
{ {
return service.ProcessControlRequest(new ControlRequest Headers = Request.Headers.ToDictionary(),
{ InputXml = requestStream,
Headers = Request.Headers.ToDictionary(), TargetServerUuId = id,
InputXml = await reader.ReadToEndAsync().ConfigureAwait(false), RequestedUrl = Request.AbsoluteUri
TargetServerUuId = id, });
RequestedUrl = Request.AbsoluteUri
});
}
} }
public object Get(GetIcon request) public object Get(GetIcon request)

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
namespace MediaBrowser.Controller.Dlna namespace MediaBrowser.Controller.Dlna
{ {
@ -6,7 +7,7 @@ namespace MediaBrowser.Controller.Dlna
{ {
public IDictionary<string, string> Headers { get; set; } public IDictionary<string, string> Headers { get; set; }
public string InputXml { get; set; } public Stream InputXml { get; set; }
public string TargetServerUuId { get; set; } public string TargetServerUuId { get; set; }

View File

@ -276,6 +276,10 @@ namespace MediaBrowser.Model.IO
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
string ReadAllText(string path, Encoding encoding); string ReadAllText(string path, Encoding encoding);
string[] ReadAllLines(string path);
void WriteAllLines(string path, IEnumerable<string> lines);
/// <summary> /// <summary>
/// Gets the directory paths. /// Gets the directory paths.
/// </summary> /// </summary>

View File

@ -137,6 +137,10 @@
<Compile Include="Dto\MetadataEditorInfo.cs" /> <Compile Include="Dto\MetadataEditorInfo.cs" />
<Compile Include="Dto\NameIdPair.cs" /> <Compile Include="Dto\NameIdPair.cs" />
<Compile Include="Dto\NameValuePair.cs" /> <Compile Include="Dto\NameValuePair.cs" />
<Compile Include="Net\IpEndPointInfo.cs" />
<Compile Include="Net\ISocketFactory.cs" />
<Compile Include="Net\IUdpSocket.cs" />
<Compile Include="Net\ReceivedUdpData.cs" />
<Compile Include="TextEncoding\IEncoding.cs" /> <Compile Include="TextEncoding\IEncoding.cs" />
<Compile Include="Extensions\LinqExtensions.cs" /> <Compile Include="Extensions\LinqExtensions.cs" />
<Compile Include="FileOrganization\SmartMatchInfo.cs" /> <Compile Include="FileOrganization\SmartMatchInfo.cs" />

View File

@ -1,15 +1,10 @@
using System; 
using System.Collections.Generic; namespace MediaBrowser.Model.Net
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Rssdp.Infrastructure
{ {
/// <summary> /// <summary>
/// Implemented by components that can create a platform specific UDP socket implementation, and wrap it in the cross platform <see cref="IUdpSocket"/> interface. /// Implemented by components that can create a platform specific UDP socket implementation, and wrap it in the cross platform <see cref="IUdpSocket"/> interface.
/// </summary> /// </summary>
public interface ISocketFactory public interface ISocketFactory
{ {
/// <summary> /// <summary>
@ -26,8 +21,6 @@ namespace Rssdp.Infrastructure
/// <param name="multicastTimeToLive">The multicast time to live value. Actually a maximum number of network hops for UDP packets.</param> /// <param name="multicastTimeToLive">The multicast time to live value. Actually a maximum number of network hops for UDP packets.</param>
/// <param name="localPort">The local port to bind to.</param> /// <param name="localPort">The local port to bind to.</param>
/// <returns>A <see cref="IUdpSocket"/> implementation.</returns> /// <returns>A <see cref="IUdpSocket"/> implementation.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "ip", Justification="IP is a well known and understood abbreviation and the full name is excessive.")]
IUdpSocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort); IUdpSocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort);
} }
} }

View File

@ -4,25 +4,24 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Rssdp.Infrastructure namespace MediaBrowser.Model.Net
{ {
/// <summary> /// <summary>
/// Provides a common interface across platforms for UDP sockets used by this SSDP implementation. /// Provides a common interface across platforms for UDP sockets used by this SSDP implementation.
/// </summary> /// </summary>
public interface IUdpSocket : IDisposable public interface IUdpSocket : IDisposable
{ {
/// <summary> /// <summary>
/// Waits for and returns the next UDP message sent to this socket (uni or multicast). /// Waits for and returns the next UDP message sent to this socket (uni or multicast).
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
System.Threading.Tasks.Task<ReceivedUdpData> ReceiveAsync(); Task<ReceivedUdpData> ReceiveAsync();
/// <summary> /// <summary>
/// Sends a UDP message to a particular end point (uni or multicast). /// Sends a UDP message to a particular end point (uni or multicast).
/// </summary> /// </summary>
/// <param name="messageData">The data to send.</param> /// <param name="messageData">The data to send.</param>
/// <param name="endPoint">The <see cref="UdpEndPoint"/> providing the address and port to send to.</param> /// <param name="endPoint">The <see cref="IpEndPointInfo"/> providing the address and port to send to.</param>
Task SendTo(byte[] messageData, UdpEndPoint endPoint); Task SendTo(byte[] messageData, IpEndPointInfo endPoint);
} }
} }

View File

@ -0,0 +1,18 @@
using System;
namespace MediaBrowser.Model.Net
{
public class IpEndPointInfo
{
public IpAddressInfo IpAddress { get; set; }
public int Port { get; set; }
public override string ToString()
{
var ipAddresString = IpAddress == null ? string.Empty : IpAddress.ToString();
return ipAddresString + ":" + this.Port.ToString();
}
}
}

View File

@ -0,0 +1,24 @@

namespace MediaBrowser.Model.Net
{
/// <summary>
/// Used by the sockets wrapper to hold raw data received from a UDP socket.
/// </summary>
public sealed class ReceivedUdpData
{
/// <summary>
/// The buffer to place received data into.
/// </summary>
public byte[] Buffer { get; set; }
/// <summary>
/// The number of bytes received.
/// </summary>
public int ReceivedBytes { get; set; }
/// <summary>
/// The <see cref="IpEndPointInfo"/> the data was received from.
/// </summary>
public IpEndPointInfo ReceivedFrom { get; set; }
}
}

View File

@ -6,5 +6,6 @@ namespace MediaBrowser.Model.Reflection
public interface IAssemblyInfo public interface IAssemblyInfo
{ {
Stream GetManifestResourceStream(Type type, string resource); Stream GetManifestResourceStream(Type type, string resource);
string[] GetManifestResourceNames(Type type);
} }
} }

View File

@ -39,14 +39,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Photos", "Emby.Photos\
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Common.Implementations", "Emby.Common.Implementations\Emby.Common.Implementations.xproj", "{5A27010A-09C6-4E86-93EA-437484C10917}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Common.Implementations", "Emby.Common.Implementations\Emby.Common.Implementations.xproj", "{5A27010A-09C6-4E86-93EA-437484C10917}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "RSSDP", "RSSDP\RSSDP.xproj", "{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.xproj", "{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Mono.Nat", "Mono.Nat\Mono.Nat.xproj", "{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Mono.Nat", "Mono.Nat\Mono.Nat.xproj", "{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -243,28 +243,6 @@ Global
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|Any CPU.Build.0 = Release|Any CPU {5A27010A-09C6-4E86-93EA-437484C10917}.Release|Any CPU.Build.0 = Release|Any CPU
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|x86.ActiveCfg = Release|Any CPU {5A27010A-09C6-4E86-93EA-437484C10917}.Release|x86.ActiveCfg = Release|Any CPU
{5A27010A-09C6-4E86-93EA-437484C10917}.Release|x86.Build.0 = Release|Any CPU {5A27010A-09C6-4E86-93EA-437484C10917}.Release|x86.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|x86.ActiveCfg = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|x86.Build.0 = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|x86.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|x86.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|Any CPU.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|x86.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|x86.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|x86.ActiveCfg = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|x86.Build.0 = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|x86.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|x86.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|Any CPU.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|x86.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|x86.Build.0 = Release|Any CPU
{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Any CPU.Build.0 = Debug|Any CPU {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|x86.ActiveCfg = Debug|Any CPU {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|x86.ActiveCfg = Debug|Any CPU
@ -289,6 +267,30 @@ Global
{E383961B-9356-4D5D-8233-9A1079D03055}.Release|Any CPU.Build.0 = Release|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Release|Any CPU.Build.0 = Release|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Release|x86.ActiveCfg = Release|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Release|x86.ActiveCfg = Release|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Release|x86.Build.0 = Release|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Release|x86.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|x86.ActiveCfg = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|x86.Build.0 = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Any CPU.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|x86.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|x86.Build.0 = Release|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
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|x86.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|x86.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}.Debug|x86.ActiveCfg = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|x86.Build.0 = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Any CPU.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|x86.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|x86.Build.0 = Release|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
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|x86.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -1,10 +1,10 @@
using MediaBrowser.Common.Net; using System;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Server.Implementations.Udp; using MediaBrowser.Server.Implementations.Udp;
using System.Net.Sockets;
namespace MediaBrowser.Server.Implementations.EntryPoints namespace MediaBrowser.Server.Implementations.EntryPoints
{ {
@ -60,7 +60,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
UdpServer = udpServer; UdpServer = udpServer;
} }
catch (SocketException ex) catch (Exception ex)
{ {
_logger.ErrorException("Failed to start UDP Server", ex); _logger.ErrorException("Failed to start UDP Server", ex);
} }

View File

@ -171,9 +171,6 @@
<Compile Include="Persistence\DataExtensions.cs" /> <Compile Include="Persistence\DataExtensions.cs" />
<Compile Include="Persistence\IDbConnector.cs" /> <Compile Include="Persistence\IDbConnector.cs" />
<Compile Include="Persistence\MediaStreamColumns.cs" /> <Compile Include="Persistence\MediaStreamColumns.cs" />
<Compile Include="Security\MBLicenseFile.cs" />
<Compile Include="Security\PluginSecurityManager.cs" />
<Compile Include="Security\RegRecord.cs" />
<Compile Include="Serialization\JsonSerializer.cs" /> <Compile Include="Serialization\JsonSerializer.cs" />
<Compile Include="Social\SharingManager.cs" /> <Compile Include="Social\SharingManager.cs" />
<Compile Include="Social\SharingRepository.cs" /> <Compile Include="Social\SharingRepository.cs" />

View File

@ -112,6 +112,7 @@ using Emby.Server.Implementations.MediaEncoder;
using Emby.Server.Implementations.Notifications; using Emby.Server.Implementations.Notifications;
using Emby.Server.Implementations.Persistence; using Emby.Server.Implementations.Persistence;
using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations.Security;
using Emby.Server.Implementations.ServerManager; using Emby.Server.Implementations.ServerManager;
using Emby.Server.Implementations.Session; using Emby.Server.Implementations.Session;
using Emby.Server.Implementations.Sync; using Emby.Server.Implementations.Sync;
@ -532,7 +533,7 @@ namespace MediaBrowser.Server.Startup.Common
{ {
await base.RegisterResources(progress).ConfigureAwait(false); await base.RegisterResources(progress).ConfigureAwait(false);
SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager, FileSystemManager); SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager, FileSystemManager, CryptographyProvider);
RegisterSingleInstance(SecurityManager); RegisterSingleInstance(SecurityManager);
InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager, CryptographyProvider); InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager, CryptographyProvider);
@ -558,7 +559,8 @@ namespace MediaBrowser.Server.Startup.Common
RegisterSingleInstance<IBlurayExaminer>(() => new BdInfoExaminer(FileSystemManager, textEncoding)); RegisterSingleInstance<IBlurayExaminer>(() => new BdInfoExaminer(FileSystemManager, textEncoding));
RegisterSingleInstance<IXmlReaderSettingsFactory>(new XmlReaderSettingsFactory()); RegisterSingleInstance<IXmlReaderSettingsFactory>(new XmlReaderSettingsFactory());
RegisterSingleInstance<IAssemblyInfo>(new AssemblyInfo()); IAssemblyInfo assemblyInfo = new AssemblyInfo();
RegisterSingleInstance<IAssemblyInfo>(assemblyInfo);
UserDataManager = new UserDataManager(LogManager, ServerConfigurationManager); UserDataManager = new UserDataManager(LogManager, ServerConfigurationManager);
RegisterSingleInstance(UserDataManager); RegisterSingleInstance(UserDataManager);
@ -648,10 +650,10 @@ namespace MediaBrowser.Server.Startup.Common
SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager, TimerFactory); SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager, TimerFactory);
RegisterSingleInstance(SessionManager); RegisterSingleInstance(SessionManager);
var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("Dlna"), JsonSerializer, this); var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("Dlna"), JsonSerializer, this, assemblyInfo);
RegisterSingleInstance<IDlnaManager>(dlnaManager); RegisterSingleInstance<IDlnaManager>(dlnaManager);
var connectionManager = new ConnectionManager(dlnaManager, ServerConfigurationManager, LogManager.GetLogger("UpnpConnectionManager"), HttpClient); var connectionManager = new ConnectionManager(dlnaManager, ServerConfigurationManager, LogManager.GetLogger("UpnpConnectionManager"), HttpClient, new XmlReaderSettingsFactory());
RegisterSingleInstance<IConnectionManager>(connectionManager); RegisterSingleInstance<IConnectionManager>(connectionManager);
CollectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("CollectionManager"), ProviderManager); CollectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("CollectionManager"), ProviderManager);
@ -666,10 +668,10 @@ namespace MediaBrowser.Server.Startup.Common
UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager);
RegisterSingleInstance(UserViewManager); RegisterSingleInstance(UserViewManager);
var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager, UserViewManager, () => MediaEncoder); var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager, UserViewManager, () => MediaEncoder, new XmlReaderSettingsFactory());
RegisterSingleInstance<IContentDirectory>(contentDirectory); RegisterSingleInstance<IContentDirectory>(contentDirectory);
var mediaRegistrar = new MediaReceiverRegistrar(LogManager.GetLogger("MediaReceiverRegistrar"), HttpClient, ServerConfigurationManager); var mediaRegistrar = new MediaReceiverRegistrar(LogManager.GetLogger("MediaReceiverRegistrar"), HttpClient, ServerConfigurationManager, new XmlReaderSettingsFactory());
RegisterSingleInstance<IMediaReceiverRegistrar>(mediaRegistrar); RegisterSingleInstance<IMediaReceiverRegistrar>(mediaRegistrar);
NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager); NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager);
@ -678,7 +680,7 @@ namespace MediaBrowser.Server.Startup.Common
SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager); SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager);
RegisterSingleInstance(SubtitleManager); RegisterSingleInstance(SubtitleManager);
RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager)); RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager, SocketFactory, TimerFactory));
ChapterManager = new ChapterManager(LibraryManager, LogManager.GetLogger("ChapterManager"), ServerConfigurationManager, ItemRepository); ChapterManager = new ChapterManager(LibraryManager, LogManager.GetLogger("ChapterManager"), ServerConfigurationManager, ItemRepository);
RegisterSingleInstance(ChapterManager); RegisterSingleInstance(ChapterManager);

View File

@ -35,9 +35,6 @@
<Reference Include="Emby.Common.Implementations"> <Reference Include="Emby.Common.Implementations">
<HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath> <HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath>
</Reference> </Reference>
<Reference Include="Emby.Dlna">
<HintPath>..\ThirdParty\emby\Emby.Dlna.dll</HintPath>
</Reference>
<Reference Include="MediaBrowser.Naming, Version=1.0.6151.30291, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="MediaBrowser.Naming, Version=1.0.6151.30291, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MediaBrowser.Naming.1.0.0.59\lib\portable-net45+win8\MediaBrowser.Naming.dll</HintPath> <HintPath>..\packages\MediaBrowser.Naming.1.0.0.59\lib\portable-net45+win8\MediaBrowser.Naming.dll</HintPath>
<Private>True</Private> <Private>True</Private>
@ -50,9 +47,6 @@
<HintPath>..\packages\Patterns.Logging.1.0.0.6\lib\portable-net45+win8\Patterns.Logging.dll</HintPath> <HintPath>..\packages\Patterns.Logging.1.0.0.6\lib\portable-net45+win8\Patterns.Logging.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="RSSDP">
<HintPath>..\ThirdParty\emby\RSSDP.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces, Version=4.0.0.0, Culture=neutral, PublicKeyToken=e06fbc6124f57c43, processorArchitecture=MSIL"> <Reference Include="ServiceStack.Interfaces, Version=4.0.0.0, Culture=neutral, PublicKeyToken=e06fbc6124f57c43, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath> <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
@ -99,6 +93,10 @@
<Compile Include="UnhandledExceptionWriter.cs" /> <Compile Include="UnhandledExceptionWriter.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj">
<Project>{805844ab-e92f-45e6-9d99-4f6d48d129a5}</Project>
<Name>Emby.Dlna</Name>
</ProjectReference>
<ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj"> <ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj">
<Project>{08fff49b-f175-4807-a2b5-73b0ebd9f716}</Project> <Project>{08fff49b-f175-4807-a2b5-73b0ebd9f716}</Project>
<Name>Emby.Drawing</Name> <Name>Emby.Drawing</Name>
@ -155,6 +153,10 @@
<Project>{4a4402d4-e910-443b-b8fc-2c18286a2ca0}</Project> <Project>{4a4402d4-e910-443b-b8fc-2c18286a2ca0}</Project>
<Name>OpenSubtitlesHandler</Name> <Name>OpenSubtitlesHandler</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\RSSDP\RSSDP.csproj">
<Project>{21002819-c39a-4d3e-be83-2a276a77fb1f}</Project>
<Name>RSSDP</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" /> <None Include="app.config" />

View File

@ -64,14 +64,14 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Common.Implementations
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BDInfo", "BDInfo\BDInfo.csproj", "{88AE38DF-19D7-406F-A6A9-09527719A21E}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BDInfo", "BDInfo\BDInfo.csproj", "{88AE38DF-19D7-406F-A6A9-09527719A21E}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "RSSDP", "RSSDP\RSSDP.xproj", "{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.xproj", "{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Mono.Nat", "Mono.Nat\Mono.Nat.xproj", "{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Mono.Nat", "Mono.Nat\Mono.Nat.xproj", "{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -566,36 +566,6 @@ Global
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|x64.Build.0 = Release|Any CPU {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|x64.Build.0 = Release|Any CPU
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|x86.ActiveCfg = Release|Any CPU {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|x86.ActiveCfg = Release|Any CPU
{88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|x86.Build.0 = Release|Any CPU {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|x86.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|Win32.ActiveCfg = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|Win32.Build.0 = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|x64.ActiveCfg = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|x64.Build.0 = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|x86.ActiveCfg = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Debug|x86.Build.0 = Debug|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|Any CPU.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|Win32.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|Win32.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|x64.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|x64.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|x86.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release Mono|x86.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|Any CPU.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|Win32.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|Win32.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|x64.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|x64.Build.0 = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|x86.ActiveCfg = Release|Any CPU
{C227ADB7-E256-4E70-A8B9-22B9E0CF4F55}.Release|x86.Build.0 = Release|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.Build.0 = Debug|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -626,36 +596,6 @@ Global
{E383961B-9356-4D5D-8233-9A1079D03055}.Release|x64.Build.0 = Release|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Release|x64.Build.0 = Release|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Release|x86.ActiveCfg = Release|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Release|x86.ActiveCfg = Release|Any CPU
{E383961B-9356-4D5D-8233-9A1079D03055}.Release|x86.Build.0 = Release|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Release|x86.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|Win32.ActiveCfg = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|Win32.Build.0 = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|x64.ActiveCfg = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|x64.Build.0 = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|x86.ActiveCfg = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Debug|x86.Build.0 = Debug|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|Any CPU.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|Win32.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|Win32.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|x64.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|x64.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|x86.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release Mono|x86.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|Any CPU.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|Win32.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|Win32.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|x64.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|x64.Build.0 = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|x86.ActiveCfg = Release|Any CPU
{F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5}.Release|x86.Build.0 = Release|Any CPU
{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Any CPU.Build.0 = Debug|Any CPU {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -686,6 +626,66 @@ Global
{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|x64.Build.0 = Release|Any CPU {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|x64.Build.0 = Release|Any CPU
{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|x86.ActiveCfg = Release|Any CPU {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|x86.ActiveCfg = Release|Any CPU
{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|x86.Build.0 = Release|Any CPU {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|x86.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Win32.ActiveCfg = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Win32.Build.0 = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|x64.ActiveCfg = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|x64.Build.0 = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|x86.ActiveCfg = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|x86.Build.0 = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Any CPU.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Win32.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|Win32.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|x64.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|x64.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|x86.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release Mono|x86.Build.0 = Release|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
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Win32.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Win32.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|x64.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|x64.Build.0 = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|x86.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|x86.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}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Win32.ActiveCfg = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Win32.Build.0 = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|x64.ActiveCfg = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|x64.Build.0 = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|x86.ActiveCfg = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|x86.Build.0 = Debug|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Any CPU.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Win32.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|Win32.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|x64.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|x64.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|x86.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release Mono|x86.Build.0 = Release|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
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Win32.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Win32.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|x64.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|x64.Build.0 = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|x86.ActiveCfg = Release|Any CPU
{805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Net;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
@ -44,8 +45,8 @@ namespace Rssdp.Infrastructure
/// Sends a message to a particular address (uni or multicast) and port. /// Sends a message to a particular address (uni or multicast) and port.
/// </summary> /// </summary>
/// <param name="messageData">A byte array containing the data to send.</param> /// <param name="messageData">A byte array containing the data to send.</param>
/// <param name="destination">A <see cref="UdpEndPoint"/> representing the destination address for the data. Can be either a multicast or unicast destination.</param> /// <param name="destination">A <see cref="IpEndPointInfo"/> representing the destination address for the data. Can be either a multicast or unicast destination.</param>
Task SendMessage(byte[] messageData, UdpEndPoint destination); Task SendMessage(byte[] messageData, IpEndPointInfo destination);
/// <summary> /// <summary>
/// Sends a message to the SSDP multicast address and port. /// Sends a message to the SSDP multicast address and port.

View File

@ -1,19 +1,30 @@
using System.Reflection; using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle("RSSDP2")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("RSSDP")] [assembly: AssemblyProduct("RSSDP2")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
// Setting ComVisible to false makes the types in this assembly not visible // Version information for an assembly consists of the following four values:
// to COM components. If you need to access a type in this assembly from //
// COM, set the ComVisible attribute to true on that type. // Major Version
[assembly: ComVisible(false)] // Minor Version
// Build Number
// The following GUID is for the ID of the typelib if this project is exposed to COM // Revision
[assembly: Guid("c227adb7-e256-4e70-a8b9-22b9e0cf4f55")] //
// 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.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -1,23 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion> <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{67F9D3A8-F71E-4428-913F-C37AE82CDB24}</ProjectGuid> <ProjectGuid>{21002819-C39A-4D3E-BE83-2A276A77FB1F}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Rssdp</RootNamespace> <RootNamespace>RSSDP</RootNamespace>
<AssemblyName>Rssdp.Portable</AssemblyName> <AssemblyName>RSSDP</AssemblyName>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <DefaultLanguage>en-US</DefaultLanguage>
<TargetFrameworkProfile>Profile44</TargetFrameworkProfile>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<NuGetPackageImportStamp>1c5b2aa5</NuGetPackageImportStamp> <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
<RestorePackages>true</RestorePackages> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -27,79 +24,57 @@
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<RunCodeAnalysis>true</RunCodeAnalysis>
<CodeAnalysisRuleSet>..\RssdpRuleset.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>bin\Debug\Rssdp.Portable.XML</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>..\lib\portable-net45+win+wpa81+wp80\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>..\RssdpRuleset.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>..\lib\portable-net45+win+wpa81+wp80\Rssdp.Portable.XML</DocumentationFile>
<RunCodeAnalysis>true</RunCodeAnalysis>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Shared\AssemblyInfoCommon.cs">
<Link>Properties\AssemblyInfoCommon.cs</Link>
</Compile>
<Compile Include="CustomHttpHeaders.cs" /> <Compile Include="CustomHttpHeaders.cs" />
<Compile Include="DeviceAvailableEventArgs.cs" /> <Compile Include="DeviceAvailableEventArgs.cs" />
<Compile Include="DeviceEventArgs.cs" /> <Compile Include="DeviceEventArgs.cs" />
<Compile Include="DeviceUnavailableEventArgs.cs" /> <Compile Include="DeviceUnavailableEventArgs.cs" />
<Compile Include="DiscoveredSsdpDevice.cs" />
<Compile Include="DisposableManagedObjectBase.cs" /> <Compile Include="DisposableManagedObjectBase.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="HttpParserBase.cs" /> <Compile Include="HttpParserBase.cs" />
<Compile Include="HttpRequestParser.cs" />
<Compile Include="HttpResponseParser.cs" />
<Compile Include="IEnumerableExtensions.cs" /> <Compile Include="IEnumerableExtensions.cs" />
<Compile Include="ISsdpCommunicationsServer.cs" /> <Compile Include="ISsdpCommunicationsServer.cs" />
<Compile Include="ISsdpDeviceLocator.cs" /> <Compile Include="ISsdpDeviceLocator.cs" />
<Compile Include="ISsdpDevicePublisher.cs" /> <Compile Include="ISsdpDevicePublisher.cs" />
<Compile Include="IUPnPDeviceValidator.cs" /> <Compile Include="IUPnPDeviceValidator.cs" />
<Compile Include="ReadOnlyEnumerable.cs" />
<Compile Include="SsdpDeviceExtensions.cs" />
<Compile Include="SsdpDeviceLocatorBase.cs" />
<Compile Include="DiscoveredSsdpDevice.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="HttpRequestParser.cs" />
<Compile Include="HttpResponseParser.cs" />
<Compile Include="ISocketFactory.cs" />
<Compile Include="IUdpSocket.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ReceivedUdpData.cs" /> <Compile Include="ReadOnlyEnumerable.cs" />
<Compile Include="RequestReceivedEventArgs.cs" /> <Compile Include="RequestReceivedEventArgs.cs" />
<Compile Include="ResponseReceivedEventArgs.cs" /> <Compile Include="ResponseReceivedEventArgs.cs" />
<Compile Include="SsdpCommunicationsServer.cs" /> <Compile Include="SsdpCommunicationsServer.cs" />
<Compile Include="SsdpConstants.cs" /> <Compile Include="SsdpConstants.cs" />
<Compile Include="SsdpDevice.cs" /> <Compile Include="SsdpDevice.cs" />
<Compile Include="SsdpDeviceExtensions.cs" />
<Compile Include="SsdpDeviceIcon.cs" /> <Compile Include="SsdpDeviceIcon.cs" />
<Compile Include="SsdpDeviceLocator.cs" />
<Compile Include="SsdpDeviceLocatorBase.cs" />
<Compile Include="SsdpDeviceProperties.cs" /> <Compile Include="SsdpDeviceProperties.cs" />
<Compile Include="SsdpDeviceProperty.cs" /> <Compile Include="SsdpDeviceProperty.cs" />
<Compile Include="SsdpDevicePublisher.cs" />
<Compile Include="SsdpDevicePublisherBase.cs" /> <Compile Include="SsdpDevicePublisherBase.cs" />
<Compile Include="SsdpEmbeddedDevice.cs" /> <Compile Include="SsdpEmbeddedDevice.cs" />
<Compile Include="SsdpRootDevice.cs" /> <Compile Include="SsdpRootDevice.cs" />
<Compile Include="UdpEndPoint.cs" />
<Compile Include="UPnP10DeviceValidator.cs" /> <Compile Include="UPnP10DeviceValidator.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<None Include="packages.config" /> <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
</ItemGroup> <Name>MediaBrowser.Model</Name>
<ItemGroup> </ProjectReference>
<CodeAnalysisDictionary Include="..\Shared\CodeAnalysisDictionary.xml">
<Link>Properties\CodeAnalysisDictionary.xml</Link>
<SubType>Designer</SubType>
</CodeAnalysisDictionary>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
</Target>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>c227adb7-e256-4e70-a8b9-22b9e0cf4f55</ProjectGuid>
<RootNamespace>RSSDP</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Rssdp.Infrastructure
{
/// <summary>
/// Used by the sockets wrapper to hold raw data received from a UDP socket.
/// </summary>
public sealed class ReceivedUdpData
{
/// <summary>
/// The buffer to place received data into.
/// </summary>
public byte[] Buffer { get; set; }
/// <summary>
/// The number of bytes received.
/// </summary>
public int ReceivedBytes { get; set; }
/// <summary>
/// The <see cref="UdpEndPoint"/> the data was received from.
/// </summary>
public UdpEndPoint ReceivedFrom { get; set; }
}
}

View File

@ -5,6 +5,7 @@ using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Net;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
@ -17,7 +18,7 @@ namespace Rssdp.Infrastructure
#region Fields #region Fields
private readonly HttpRequestMessage _Message; private readonly HttpRequestMessage _Message;
private readonly UdpEndPoint _ReceivedFrom; private readonly IpEndPointInfo _ReceivedFrom;
#endregion #endregion
@ -28,7 +29,7 @@ namespace Rssdp.Infrastructure
/// </summary> /// </summary>
/// <param name="message">The <see cref="HttpRequestMessage"/> that was received.</param> /// <param name="message">The <see cref="HttpRequestMessage"/> that was received.</param>
/// <param name="receivedFrom">A <see cref="UdpEndPoint"/> representing the sender's address (sometimes used for replies).</param> /// <param name="receivedFrom">A <see cref="UdpEndPoint"/> representing the sender's address (sometimes used for replies).</param>
public RequestReceivedEventArgs(HttpRequestMessage message, UdpEndPoint receivedFrom) public RequestReceivedEventArgs(HttpRequestMessage message, IpEndPointInfo receivedFrom)
{ {
_Message = message; _Message = message;
_ReceivedFrom = receivedFrom; _ReceivedFrom = receivedFrom;
@ -49,7 +50,7 @@ namespace Rssdp.Infrastructure
/// <summary> /// <summary>
/// The <see cref="UdpEndPoint"/> the request came from. /// The <see cref="UdpEndPoint"/> the request came from.
/// </summary> /// </summary>
public UdpEndPoint ReceivedFrom public IpEndPointInfo ReceivedFrom
{ {
get { return _ReceivedFrom; } get { return _ReceivedFrom; }
} }

View File

@ -5,6 +5,7 @@ using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Net;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
@ -17,7 +18,7 @@ namespace Rssdp.Infrastructure
#region Fields #region Fields
private readonly HttpResponseMessage _Message; private readonly HttpResponseMessage _Message;
private readonly UdpEndPoint _ReceivedFrom; private readonly IpEndPointInfo _ReceivedFrom;
#endregion #endregion
@ -26,9 +27,7 @@ namespace Rssdp.Infrastructure
/// <summary> /// <summary>
/// Full constructor. /// Full constructor.
/// </summary> /// </summary>
/// <param name="message">The <see cref="HttpResponseMessage"/> that was received.</param> public ResponseReceivedEventArgs(HttpResponseMessage message, IpEndPointInfo receivedFrom)
/// <param name="receivedFrom">A <see cref="UdpEndPoint"/> representing the sender's address (sometimes used for replies).</param>
public ResponseReceivedEventArgs(HttpResponseMessage message, UdpEndPoint receivedFrom)
{ {
_Message = message; _Message = message;
_ReceivedFrom = receivedFrom; _ReceivedFrom = receivedFrom;
@ -49,7 +48,7 @@ namespace Rssdp.Infrastructure
/// <summary> /// <summary>
/// The <see cref="UdpEndPoint"/> the response came from. /// The <see cref="UdpEndPoint"/> the response came from.
/// </summary> /// </summary>
public UdpEndPoint ReceivedFrom public IpEndPointInfo ReceivedFrom
{ {
get { return _ReceivedFrom; } get { return _ReceivedFrom; }
} }

View File

@ -1,114 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security;
using System.Text;
using Rssdp.Infrastructure;
namespace Rssdp
{
// THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS
// Be careful to check any changes compile and work for all platform projects it is shared in.
// Not entirely happy with this. Would have liked to have done something more generic/reusable,
// but that wasn't really the point so kept to YAGNI principal for now, even if the
// interfaces are a bit ugly, specific and make assumptions.
/// <summary>
/// Used by RSSDP components to create implementations of the <see cref="IUdpSocket"/> interface, to perform platform agnostic socket communications.
/// </summary>
public sealed class SocketFactory : ISocketFactory
{
private IPAddress _LocalIP;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="localIP">A string containing the IP address of the local network adapter to bind sockets to. Null or empty string will use <see cref="IPAddress.Any"/>.</param>
public SocketFactory(string localIP)
{
if (String.IsNullOrEmpty(localIP))
_LocalIP = IPAddress.Any;
else
_LocalIP = IPAddress.Parse(localIP);
}
#region ISocketFactory Members
/// <summary>
/// Creates a new UDP socket that is a member of the SSDP multicast local admin group and binds it to the specified local port.
/// </summary>
/// <param name="localPort">An integer specifying the local port to bind the socket to.</param>
/// <returns>An implementation of the <see cref="IUdpSocket"/> interface used by RSSDP components to perform socket operations.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The purpose of this method is to create and returns a disposable result, it is up to the caller to dispose it when they are done with it.")]
public IUdpSocket CreateUdpSocket(int localPort)
{
if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort");
var retVal = new Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
try
{
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, SsdpConstants.SsdpDefaultMulticastTimeToLive);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse(SsdpConstants.MulticastLocalAdminAddress), _LocalIP));
return new UdpSocket(retVal, localPort, _LocalIP.ToString());
}
catch
{
if (retVal != null)
retVal.Dispose();
throw;
}
}
/// <summary>
/// Creates a new UDP socket that is a member of the specified multicast IP address, and binds it to the specified local port.
/// </summary>
/// <param name="ipAddress">The multicast IP address to make the socket a member of.</param>
/// <param name="multicastTimeToLive">The multicast time to live value for the socket.</param>
/// <param name="localPort">The number of the local port to bind to.</param>
/// <returns></returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "ip"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The purpose of this method is to create and returns a disposable result, it is up to the caller to dispose it when they are done with it.")]
public IUdpSocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort)
{
if (ipAddress == null) throw new ArgumentNullException("ipAddress");
if (ipAddress.Length == 0) throw new ArgumentException("ipAddress cannot be an empty string.", "ipAddress");
if (multicastTimeToLive <= 0) throw new ArgumentException("multicastTimeToLive cannot be zero or less.", "multicastTimeToLive");
if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort");
var retVal = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
try
{
#if NETSTANDARD1_3
// The ExclusiveAddressUse socket option is a Windows-specific option that, when set to "true," tells Windows not to allow another socket to use the same local address as this socket
// See https://github.com/dotnet/corefx/pull/11509 for more details
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
{
retVal.ExclusiveAddressUse = false;
}
#else
retVal.ExclusiveAddressUse = false;
#endif
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse(ipAddress), _LocalIP));
retVal.MulticastLoopback = true;
return new UdpSocket(retVal, localPort, _LocalIP.ToString());
}
catch
{
if (retVal != null)
retVal.Dispose();
throw;
}
}
#endregion
}
}

View File

@ -5,6 +5,7 @@ using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Net;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
@ -157,10 +158,10 @@ namespace Rssdp.Infrastructure
/// Sends a message to a particular address (uni or multicast) and port. /// Sends a message to a particular address (uni or multicast) and port.
/// </summary> /// </summary>
/// <param name="messageData">A byte array containing the data to send.</param> /// <param name="messageData">A byte array containing the data to send.</param>
/// <param name="destination">A <see cref="UdpEndPoint"/> representing the destination address for the data. Can be either a multicast or unicast destination.</param> /// <param name="destination">A <see cref="IpEndPointInfo"/> representing the destination address for the data. Can be either a multicast or unicast destination.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="messageData"/> argument is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="messageData"/> argument is null.</exception>
/// <exception cref="System.ObjectDisposedException">Thrown if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true (because <seealso cref="DisposableManagedObjectBase.Dispose()" /> has been called previously).</exception> /// <exception cref="System.ObjectDisposedException">Thrown if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true (because <seealso cref="DisposableManagedObjectBase.Dispose()" /> has been called previously).</exception>
public async Task SendMessage(byte[] messageData, UdpEndPoint destination) public async Task SendMessage(byte[] messageData, IpEndPointInfo destination)
{ {
if (messageData == null) throw new ArgumentNullException("messageData"); if (messageData == null) throw new ArgumentNullException("messageData");
@ -188,7 +189,7 @@ namespace Rssdp.Infrastructure
// SSDP spec recommends sending messages multiple times (not more than 3) to account for possible packet loss over UDP. // SSDP spec recommends sending messages multiple times (not more than 3) to account for possible packet loss over UDP.
await Repeat(SsdpConstants.UdpResendCount, TimeSpan.FromMilliseconds(100), await Repeat(SsdpConstants.UdpResendCount, TimeSpan.FromMilliseconds(100),
() => SendMessageIfSocketNotDisposed(messageData, new UdpEndPoint() { IPAddress = SsdpConstants.MulticastLocalAdminAddress, Port = SsdpConstants.MulticastPort })).ConfigureAwait(false); () => SendMessageIfSocketNotDisposed(messageData, new IpEndPointInfo() { IpAddress = new IpAddressInfo { Address = SsdpConstants.MulticastLocalAdminAddress }, Port = SsdpConstants.MulticastPort })).ConfigureAwait(false);
} }
/// <summary> /// <summary>
@ -254,7 +255,7 @@ namespace Rssdp.Infrastructure
#region Private Methods #region Private Methods
private async Task SendMessageIfSocketNotDisposed(byte[] messageData, UdpEndPoint destination) private async Task SendMessageIfSocketNotDisposed(byte[] messageData, IpEndPointInfo destination)
{ {
var socket = _SendSocket; var socket = _SendSocket;
if (socket != null) if (socket != null)
@ -343,7 +344,7 @@ namespace Rssdp.Infrastructure
} }
} }
private void ProcessMessage(string data, UdpEndPoint endPoint) private void ProcessMessage(string data, IpEndPointInfo endPoint)
{ {
//Responses start with the HTTP version, prefixed with HTTP/ while //Responses start with the HTTP version, prefixed with HTTP/ while
//requests start with a method which can vary and might be one we haven't //requests start with a method which can vary and might be one we haven't
@ -375,7 +376,7 @@ namespace Rssdp.Infrastructure
} }
} }
private void OnRequestReceived(HttpRequestMessage data, UdpEndPoint endPoint) private void OnRequestReceived(HttpRequestMessage data, IpEndPointInfo endPoint)
{ {
//SSDP specification says only * is currently used but other uri's might //SSDP specification says only * is currently used but other uri's might
//be implemented in the future and should be ignored unless understood. //be implemented in the future and should be ignored unless understood.
@ -387,7 +388,7 @@ namespace Rssdp.Infrastructure
handlers(this, new RequestReceivedEventArgs(data, endPoint)); handlers(this, new RequestReceivedEventArgs(data, endPoint));
} }
private void OnResponseReceived(HttpResponseMessage data, UdpEndPoint endPoint) private void OnResponseReceived(HttpResponseMessage data, IpEndPointInfo endPoint)
{ {
var handlers = this.ResponseReceived; var handlers = this.ResponseReceived;
if (handlers != null) if (handlers != null)

View File

@ -2,6 +2,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Threading;
using Rssdp.Infrastructure; using Rssdp.Infrastructure;
namespace Rssdp namespace Rssdp
@ -19,7 +21,7 @@ namespace Rssdp
/// Default constructor. Constructs a new instance using the default <see cref="ISsdpCommunicationsServer"/> and <see cref="ISocketFactory"/> implementations for this platform. /// Default constructor. Constructs a new instance using the default <see cref="ISsdpCommunicationsServer"/> and <see cref="ISocketFactory"/> implementations for this platform.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification="Can't expose along exception paths here (exceptions should be very rare anyway, and probably fatal too) and we shouldn't dipose the items we pass to base in any other case.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification="Can't expose along exception paths here (exceptions should be very rare anyway, and probably fatal too) and we shouldn't dipose the items we pass to base in any other case.")]
public SsdpDeviceLocator() : base(new SsdpCommunicationsServer(new SocketFactory(null))) public SsdpDeviceLocator(ISocketFactory socketFactory, ITimerFactory timerFacatory) : base(new SsdpCommunicationsServer(socketFactory), timerFacatory)
{ {
// This is not the problem you are looking for; // This is not the problem you are looking for;
// Yes, this is poor man's dependency injection which some call an anti-pattern. // Yes, this is poor man's dependency injection which some call an anti-pattern.
@ -30,14 +32,5 @@ namespace Rssdp
// There is a constructor that takes a manually injected dependency anyway, so proper DI using // There is a constructor that takes a manually injected dependency anyway, so proper DI using
// a container or whatever can be done anyway. // a container or whatever can be done anyway.
} }
/// <summary>
/// Full constructor. Constructs a new instance using the provided <see cref="ISsdpCommunicationsServer"/> implementation.
/// </summary>
public SsdpDeviceLocator(ISsdpCommunicationsServer communicationsServer)
: base(communicationsServer)
{
}
} }
} }

View File

@ -7,6 +7,7 @@ using System.Net.Http;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Threading;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
@ -24,7 +25,8 @@ namespace Rssdp.Infrastructure
private IList<DiscoveredSsdpDevice> _SearchResults; private IList<DiscoveredSsdpDevice> _SearchResults;
private object _SearchResultsSynchroniser; private object _SearchResultsSynchroniser;
private System.Threading.Timer _ExpireCachedDevicesTimer; private ITimer _ExpireCachedDevicesTimer;
private ITimerFactory _timerFactory;
private const string HttpURequestMessageFormat = @"{0} * HTTP/1.1 private const string HttpURequestMessageFormat = @"{0} * HTTP/1.1
HOST: {1}:{2} HOST: {1}:{2}
@ -44,12 +46,12 @@ ST: {4}
/// <summary> /// <summary>
/// Default constructor. /// Default constructor.
/// </summary> /// </summary>
/// <param name="communicationsServer">The <see cref="ISsdpCommunicationsServer"/> implementation to use for network communications.</param> protected SsdpDeviceLocatorBase(ISsdpCommunicationsServer communicationsServer, ITimerFactory timerFactory)
protected SsdpDeviceLocatorBase(ISsdpCommunicationsServer communicationsServer)
{ {
if (communicationsServer == null) throw new ArgumentNullException("communicationsServer"); if (communicationsServer == null) throw new ArgumentNullException("communicationsServer");
_CommunicationsServer = communicationsServer; _CommunicationsServer = communicationsServer;
_timerFactory = timerFactory;
_CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived; _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived;
_SearchResultsSynchroniser = new object(); _SearchResultsSynchroniser = new object();
@ -521,7 +523,7 @@ ST: {4}
if (IsDisposed) return; if (IsDisposed) return;
if (_ExpireCachedDevicesTimer == null) if (_ExpireCachedDevicesTimer == null)
_ExpireCachedDevicesTimer = new Timer(this.ExpireCachedDevices, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); _ExpireCachedDevicesTimer = _timerFactory.Create(this.ExpireCachedDevices, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
_ExpireCachedDevicesTimer.Change(60000, System.Threading.Timeout.Infinite); _ExpireCachedDevicesTimer.Change(60000, System.Threading.Timeout.Infinite);
} }

View File

@ -2,6 +2,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Threading;
using Rssdp.Infrastructure; using Rssdp.Infrastructure;
namespace Rssdp namespace Rssdp
@ -24,79 +26,13 @@ namespace Rssdp
/// <para>Uses the default <see cref="ISsdpCommunicationsServer"/> implementation and network settings for Windows and the SSDP specification.</para> /// <para>Uses the default <see cref="ISsdpCommunicationsServer"/> implementation and network settings for Windows and the SSDP specification.</para>
/// </remarks> /// </remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No way to do this here, and we don't want to dispose it except in the (rare) case of an exception anyway.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No way to do this here, and we don't want to dispose it except in the (rare) case of an exception anyway.")]
public SsdpDevicePublisher() public SsdpDevicePublisher(ISocketFactory socketFactory, ITimerFactory timerFactory, string osName, string osVersion)
: this(new SsdpCommunicationsServer(new SocketFactory(null))) : base(new SsdpCommunicationsServer(socketFactory), timerFactory, osName, osVersion)
{ {
} }
/// <summary>
/// Full constructor.
/// </summary>
/// <remarks>
/// <para>Allows the caller to specify their own <see cref="ISsdpCommunicationsServer"/> implementation for full control over the networking, or for mocking/testing purposes..</para>
/// </remarks>
public SsdpDevicePublisher(ISsdpCommunicationsServer communicationsServer)
: base(communicationsServer, GetOSName(), GetOSVersion())
{
}
/// <summary>
/// Partial constructor.
/// </summary>
/// <param name="localPort">The local port to use for socket communications, specify 0 to have the system choose it's own.</param>
/// <remarks>
/// <para>Uses the default <see cref="ISsdpCommunicationsServer"/> implementation and network settings for Windows and the SSDP specification, but specifies the local port to use for socket communications. Specify 0 to indicate the system should choose it's own port.</para>
/// </remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No way to do this here, and we don't want to dispose it except in the (rare) case of an exception anyway.")]
public SsdpDevicePublisher(int localPort)
: this(new SsdpCommunicationsServer(new SocketFactory(null), localPort))
{
}
/// <summary>
/// Partial constructor.
/// </summary>
/// <param name="localPort">The local port to use for socket communications, specify 0 to have the system choose it's own.</param>
/// <param name="multicastTimeToLive">The number of hops a multicast packet can make before it expires. Must be 1 or greater.</param>
/// <remarks>
/// <para>Uses the default <see cref="ISsdpCommunicationsServer"/> implementation and network settings for Windows and the SSDP specification, but specifies the local port to use and multicast time to live setting for socket communications.</para>
/// <para>Specify 0 for the <paramref name="localPort"/> argument to indicate the system should choose it's own port.</para>
/// <para>The <paramref name="multicastTimeToLive"/> is actually a number of 'hops' on the network and not a time based argument.</para>
/// </remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No way to do this here, and we don't want to dispose it except in the (rare) case of an exception anyway.")]
public SsdpDevicePublisher(int localPort, int multicastTimeToLive)
: this(new SsdpCommunicationsServer(new SocketFactory(null), localPort, multicastTimeToLive))
{
}
#endregion #endregion
#region Private Methods
private static string GetOSName()
{
#if NET46
return Environment.OSVersion.Platform.ToString();
#elif NETSTANDARD1_6
return System.Runtime.InteropServices.RuntimeInformation.OSDescription;
#endif
return "Operating System";
}
private static string GetOSVersion()
{
#if NET46
return Environment.OSVersion.Version.ToString() + " " + Environment.OSVersion.ServicePack.ToString();
#elif NETSTANDARD1_6
return System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;
#endif
return "1.0";
}
#endregion
} }
} }

View File

@ -4,6 +4,8 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Threading;
namespace Rssdp.Infrastructure namespace Rssdp.Infrastructure
{ {
@ -24,7 +26,8 @@ namespace Rssdp.Infrastructure
private IList<SsdpRootDevice> _Devices; private IList<SsdpRootDevice> _Devices;
private ReadOnlyEnumerable<SsdpRootDevice> _ReadOnlyDevices; private ReadOnlyEnumerable<SsdpRootDevice> _ReadOnlyDevices;
private System.Threading.Timer _RebroadcastAliveNotificationsTimer; private ITimer _RebroadcastAliveNotificationsTimer;
private ITimerFactory _timerFactory;
//private TimeSpan _RebroadcastAliveNotificationsTimeSpan; //private TimeSpan _RebroadcastAliveNotificationsTimeSpan;
private DateTime _LastNotificationTime; private DateTime _LastNotificationTime;
@ -47,10 +50,7 @@ namespace Rssdp.Infrastructure
/// <summary> /// <summary>
/// Default constructor. /// Default constructor.
/// </summary> /// </summary>
/// <param name="communicationsServer">The <see cref="ISsdpCommunicationsServer"/> implementation, used to send and receive SSDP network messages.</param> protected SsdpDevicePublisherBase(ISsdpCommunicationsServer communicationsServer, ITimerFactory timerFactory, string osName, string osVersion)
/// <param name="osName">Then name of the operating system running the server.</param>
/// <param name="osVersion">The version of the operating system running the server.</param>
protected SsdpDevicePublisherBase(ISsdpCommunicationsServer communicationsServer, string osName, string osVersion)
{ {
if (communicationsServer == null) throw new ArgumentNullException("communicationsServer"); if (communicationsServer == null) throw new ArgumentNullException("communicationsServer");
if (osName == null) throw new ArgumentNullException("osName"); if (osName == null) throw new ArgumentNullException("osName");
@ -59,6 +59,7 @@ namespace Rssdp.Infrastructure
if (osVersion.Length == 0) throw new ArgumentException("osVersion cannot be an empty string.", "osName"); if (osVersion.Length == 0) throw new ArgumentException("osVersion cannot be an empty string.", "osName");
_SupportPnpRootDevice = true; _SupportPnpRootDevice = true;
_timerFactory = timerFactory;
_Devices = new List<SsdpRootDevice>(); _Devices = new List<SsdpRootDevice>();
_ReadOnlyDevices = new ReadOnlyEnumerable<SsdpRootDevice>(_Devices); _ReadOnlyDevices = new ReadOnlyEnumerable<SsdpRootDevice>(_Devices);
_RecentSearchRequests = new Dictionary<string, SearchRequest>(StringComparer.OrdinalIgnoreCase); _RecentSearchRequests = new Dictionary<string, SearchRequest>(StringComparer.OrdinalIgnoreCase);
@ -234,7 +235,7 @@ namespace Rssdp.Infrastructure
#region Search Related Methods #region Search Related Methods
private void ProcessSearchRequest(string mx, string searchTarget, UdpEndPoint endPoint) private void ProcessSearchRequest(string mx, string searchTarget, IpEndPointInfo endPoint)
{ {
if (String.IsNullOrEmpty(searchTarget)) if (String.IsNullOrEmpty(searchTarget))
{ {
@ -305,7 +306,7 @@ namespace Rssdp.Infrastructure
return _Devices.Union(_Devices.SelectManyRecursive<SsdpDevice>((d) => d.Devices)); return _Devices.Union(_Devices.SelectManyRecursive<SsdpDevice>((d) => d.Devices));
} }
private void SendDeviceSearchResponses(SsdpDevice device, UdpEndPoint endPoint) private void SendDeviceSearchResponses(SsdpDevice device, IpEndPointInfo endPoint)
{ {
bool isRootDevice = (device as SsdpRootDevice) != null; bool isRootDevice = (device as SsdpRootDevice) != null;
if (isRootDevice) if (isRootDevice)
@ -325,7 +326,7 @@ namespace Rssdp.Infrastructure
return String.Format("{0}::{1}", udn, fullDeviceType); return String.Format("{0}::{1}", udn, fullDeviceType);
} }
private async void SendSearchResponse(string searchTarget, SsdpDevice device, string uniqueServiceName, UdpEndPoint endPoint) private async void SendSearchResponse(string searchTarget, SsdpDevice device, string uniqueServiceName, IpEndPointInfo endPoint)
{ {
var rootDevice = device.ToRootDevice(); var rootDevice = device.ToRootDevice();
@ -357,7 +358,7 @@ namespace Rssdp.Infrastructure
WriteTrace(String.Format("Sent search response to " + endPoint.ToString()), device); WriteTrace(String.Format("Sent search response to " + endPoint.ToString()), device);
} }
private bool IsDuplicateSearchRequest(string searchTarget, UdpEndPoint endPoint) private bool IsDuplicateSearchRequest(string searchTarget, IpEndPointInfo endPoint)
{ {
var isDuplicateRequest = false; var isDuplicateRequest = false;
@ -590,7 +591,7 @@ namespace Rssdp.Infrastructure
} }
//_RebroadcastAliveNotificationsTimeSpan = rebroadCastInterval; //_RebroadcastAliveNotificationsTimeSpan = rebroadCastInterval;
_RebroadcastAliveNotificationsTimer = new System.Threading.Timer(SendAllAliveNotifications, null, nextBroadcastInterval, rebroadCastInterval); _RebroadcastAliveNotificationsTimer = _timerFactory.Create(SendAllAliveNotifications, null, nextBroadcastInterval, rebroadCastInterval);
WriteTrace(String.Format("Rebroadcast Interval = {0}, Next Broadcast At = {1}", rebroadCastInterval.ToString(), nextBroadcastInterval.ToString())); WriteTrace(String.Format("Rebroadcast Interval = {0}, Next Broadcast At = {1}", rebroadCastInterval.ToString(), nextBroadcastInterval.ToString()));
} }
@ -704,7 +705,7 @@ namespace Rssdp.Infrastructure
private class SearchRequest private class SearchRequest
{ {
public UdpEndPoint EndPoint { get; set; } public IpEndPointInfo EndPoint { get; set; }
public DateTime Received { get; set; } public DateTime Received { get; set; }
public string SearchTarget { get; set; } public string SearchTarget { get; set; }

View File

@ -1,37 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Rssdp.Infrastructure
{
/// <summary>
/// Cross platform representation of a UDP end point, being an IP address (either IPv4 or IPv6) and a port.
/// </summary>
public sealed class UdpEndPoint
{
/// <summary>
/// The IP Address of the end point.
/// </summary>
/// <remarks>
/// <para>Can be either IPv4 or IPv6, up to the code using this instance to determine which was provided.</para>
/// </remarks>
public string IPAddress { get; set; }
/// <summary>
/// The port of the end point.
/// </summary>
public int Port { get; set; }
/// <summary>
/// Returns the <see cref="IPAddress"/> and <see cref="Port"/> values separated by a colon.
/// </summary>
/// <returns>A string containing <see cref="IPAddress"/>:<see cref="Port"/>.</returns>
public override string ToString()
{
return (this.IPAddress ?? String.Empty) + ":" + this.Port.ToString();
}
}
}

View File

@ -1,48 +0,0 @@
{
"version": "1.0.0-*",
"dependencies": {
},
"frameworks": {
"net46": {
"frameworkAssemblies": {
"System.Collections": "4.0.0.0",
"System.Net": "4.0.0.0",
"System.Net.Http": "4.0.0.0",
"System.Runtime": "4.0.0.0",
"System.Threading": "4.0.0.0",
"System.Threading.Tasks": "4.0.0.0",
"System.Xml": "4.0.0.0"
},
"dependencies": {
}
},
"netstandard1.6": {
"imports": "dnxcore50",
"dependencies": {
"NETStandard.Library": "1.6.0",
"System.Collections": "4.0.11",
"System.Diagnostics.Debug": "4.0.11",
"System.Diagnostics.Tools": "4.0.1",
"System.IO": "4.1.0",
"System.Linq": "4.1.0",
"System.Net.Http": "4.1.0",
"System.Net.Primitives": "4.0.11",
"System.Net.Sockets": "4.1.0",
"System.Resources.ResourceManager": "4.0.1",
"System.Runtime": "4.1.0",
"System.Runtime.Extensions": "4.1.0",
"System.Runtime.InteropServices.RuntimeInformation": "4.0.0",
"System.Text.Encoding": "4.0.11",
"System.Text.Encoding.Extensions": "4.0.11",
"System.Threading": "4.0.11",
"System.Threading.Tasks": "4.0.11",
"System.Threading.Timer": "4.0.1",
"System.Xml.ReaderWriter": "4.0.11"
}
}
}
}