support sending upnp events

This commit is contained in:
Luke Pulverenti 2014-04-21 12:02:30 -04:00
parent 3b4be92038
commit 4331700747
22 changed files with 526 additions and 150 deletions

View File

@ -51,10 +51,14 @@ namespace MediaBrowser.Api.Dlna
public class DlnaServerService : BaseApiService
{
private readonly IDlnaManager _dlnaManager;
private readonly IContentDirectory _contentDirectory;
private readonly IEventManager _eventManager;
public DlnaServerService(IDlnaManager dlnaManager)
public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IEventManager eventManager)
{
_dlnaManager = dlnaManager;
_contentDirectory = contentDirectory;
_eventManager = eventManager;
}
public object Get(GetDescriptionXml request)
@ -66,7 +70,7 @@ namespace MediaBrowser.Api.Dlna
public object Get(GetContentDirectory request)
{
var xml = _dlnaManager.GetContentDirectoryXml(GetRequestHeaders());
var xml = _contentDirectory.GetContentDirectoryXml(GetRequestHeaders());
return ResultFactory.GetResult(xml, "text/xml");
}
@ -85,7 +89,7 @@ namespace MediaBrowser.Api.Dlna
using (var reader = new StreamReader(request.RequestStream))
{
return _dlnaManager.ProcessControlRequest(new ControlRequest
return _contentDirectory.ProcessControlRequest(new ControlRequest
{
Headers = GetRequestHeaders(),
InputXml = await reader.ReadToEndAsync().ConfigureAwait(false),
@ -128,49 +132,24 @@ namespace MediaBrowser.Api.Dlna
var callback = GetHeader("CALLBACK");
var timeoutString = GetHeader("TIMEOUT");
var timeout = ParseTimeout(timeoutString) ?? 300;
var timeout = ParseTimeout(timeoutString);
if (string.Equals(Request.Verb, "SUBSCRIBE", StringComparison.OrdinalIgnoreCase))
{
if (string.IsNullOrEmpty(notificationType))
{
RenewEvent(subscriptionId, timeout);
}
else
{
SubscribeToEvent(notificationType, timeout, callback);
return GetSubscriptionResponse(_eventManager.RenewEventSubscription(subscriptionId, timeout));
}
return GetSubscriptionResponse(request.UuId, timeout);
return GetSubscriptionResponse(_eventManager.CreateEventSubscription(notificationType, timeout, callback));
}
UnsubscribeFromEvent(subscriptionId);
return ResultFactory.GetResult("", "text/plain");
return GetSubscriptionResponse(_eventManager.CancelEventSubscription(subscriptionId));
}
private void UnsubscribeFromEvent(string subscriptionId)
private object GetSubscriptionResponse(EventSubscriptionResponse response)
{
}
private void SubscribeToEvent(string notificationType, int? timeout, string callback)
{
}
private void RenewEvent(string subscriptionId, int? timeout)
{
}
private object GetSubscriptionResponse(string uuid, int timeout)
{
var headers = new Dictionary<string, string>();
headers["SID"] = "uuid:" + uuid;
headers["TIMEOUT"] = "SECOND-" + timeout.ToString(_usCulture);
return ResultFactory.GetResult("\r\n", "text/plain", headers);
return ResultFactory.GetResult(response.Content, response.ContentType, response.Headers);
}
private readonly CultureInfo _usCulture = new CultureInfo("en-US");

View File

@ -215,7 +215,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
/// <returns>Task{HttpResponseInfo}.</returns>
/// <exception cref="HttpException">
/// </exception>
private async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
{
ValidateParams(options);

View File

@ -43,6 +43,14 @@ namespace MediaBrowser.Common.Net
/// <returns>Task{Stream}.</returns>
Task<Stream> Get(HttpRequestOptions options);
/// <summary>
/// Sends the asynchronous.
/// </summary>
/// <param name="options">The options.</param>
/// <param name="httpMethod">The HTTP method.</param>
/// <returns>Task{HttpResponseInfo}.</returns>
Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod);
/// <summary>
/// Performs a POST request
/// </summary>

View File

@ -0,0 +1,17 @@
using System.Collections.Generic;
namespace MediaBrowser.Controller.Dlna
{
public class EventSubscriptionResponse
{
public string Content { get; set; }
public string ContentType { get; set; }
public Dictionary<string, string> Headers { get; set; }
public EventSubscriptionResponse()
{
Headers = new Dictionary<string, string>();
}
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace MediaBrowser.Controller.Dlna
{
public interface IContentDirectory
{
/// <summary>
/// Gets the content directory XML.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns>System.String.</returns>
string GetContentDirectoryXml(IDictionary<string, string> headers);
/// <summary>
/// Processes the control request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>ControlResponse.</returns>
ControlResponse ProcessControlRequest(ControlRequest request);
}
}

View File

@ -64,20 +64,6 @@ namespace MediaBrowser.Controller.Dlna
/// <returns>System.String.</returns>
string GetServerDescriptionXml(IDictionary<string, string> headers, string serverUuId);
/// <summary>
/// Gets the content directory XML.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns>System.String.</returns>
string GetContentDirectoryXml(IDictionary<string, string> headers);
/// <summary>
/// Processes the control request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>ControlResponse.</returns>
ControlResponse ProcessControlRequest(ControlRequest request);
/// <summary>
/// Gets the icon.
/// </summary>

View File

@ -0,0 +1,47 @@
using MediaBrowser.Model.Dlna;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Dlna
{
public interface IEventManager
{
/// <summary>
/// Cancels the event subscription.
/// </summary>
/// <param name="subscriptionId">The subscription identifier.</param>
EventSubscriptionResponse CancelEventSubscription(string subscriptionId);
/// <summary>
/// Renews the event subscription.
/// </summary>
/// <param name="subscriptionId">The subscription identifier.</param>
/// <param name="timeoutSeconds">The timeout seconds.</param>
/// <returns>EventSubscriptionResponse.</returns>
EventSubscriptionResponse RenewEventSubscription(string subscriptionId, int? timeoutSeconds);
/// <summary>
/// Creates the event subscription.
/// </summary>
/// <param name="notificationType">Type of the notification.</param>
/// <param name="timeoutSeconds">The timeout seconds.</param>
/// <param name="callbackUrl">The callback URL.</param>
/// <returns>EventSubscriptionResponse.</returns>
EventSubscriptionResponse CreateEventSubscription(string notificationType, int? timeoutSeconds, string callbackUrl);
/// <summary>
/// Gets the subscription.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>EventSubscription.</returns>
EventSubscription GetSubscription(string id);
/// <summary>
/// Triggers the event.
/// </summary>
/// <param name="notificationType">Type of the notification.</param>
/// <param name="stateVariables">The state variables.</param>
/// <returns>Task.</returns>
Task TriggerEvent(string notificationType, IDictionary<string,string> stateVariables);
}
}

View File

@ -80,7 +80,10 @@
<Compile Include="Collections\ICollectionManager.cs" />
<Compile Include="Dlna\ControlRequest.cs" />
<Compile Include="Dlna\DlnaIconResponse.cs" />
<Compile Include="Dlna\EventSubscriptionResponse.cs" />
<Compile Include="Dlna\IContentDirectory.cs" />
<Compile Include="Dlna\IDlnaManager.cs" />
<Compile Include="Dlna\IEventManager.cs" />
<Compile Include="Drawing\IImageProcessor.cs" />
<Compile Include="Drawing\ImageFormat.cs" />
<Compile Include="Drawing\ImageProcessingOptions.cs" />

View File

@ -1,12 +1,8 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Dlna.Profiles;
using MediaBrowser.Dlna.Server;
using MediaBrowser.Model.Dlna;
@ -28,28 +24,20 @@ namespace MediaBrowser.Dlna
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
private readonly IJsonSerializer _jsonSerializer;
private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
private readonly IUserDataManager _userDataManager;
private readonly IServerConfigurationManager _config;
public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IApplicationPaths appPaths, ILogger logger, IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager, IServerConfigurationManager config)
public DlnaManager(IXmlSerializer xmlSerializer,
IFileSystem fileSystem,
IApplicationPaths appPaths,
ILogger logger,
IJsonSerializer jsonSerializer)
{
_xmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
_appPaths = appPaths;
_logger = logger;
_jsonSerializer = jsonSerializer;
_userManager = userManager;
_libraryManager = libraryManager;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
_userDataManager = userDataManager;
_config = config;
DumpProfiles();
//DumpProfiles();
}
public IEnumerable<DeviceProfile> GetProfiles()
@ -499,37 +487,6 @@ namespace MediaBrowser.Dlna
return new DescriptionXmlBuilder(profile, serverUuId).GetXml();
}
public string GetContentDirectoryXml(IDictionary<string, string> headers)
{
var profile = GetProfile(headers) ??
GetDefaultProfile();
return new ContentDirectoryXmlBuilder(profile).GetXml();
}
public ControlResponse ProcessControlRequest(ControlRequest request)
{
var profile = GetProfile(request.Headers)
?? GetDefaultProfile();
var device = DlnaServerEntryPoint.Instance.GetServerUpnpDevice(request.TargetServerUuId);
var serverAddress = device.Descriptor.ToString().Substring(0, device.Descriptor.ToString().IndexOf("/dlna", StringComparison.OrdinalIgnoreCase));
var user = GetUser(profile);
return new ControlHandler(
_logger,
_libraryManager,
profile,
serverAddress,
_dtoService,
_imageProcessor,
_userDataManager,
user)
.ProcessControlRequest(request);
}
public DlnaIconResponse GetIcon(string filename)
{
var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
@ -542,33 +499,5 @@ namespace MediaBrowser.Dlna
Stream = GetType().Assembly.GetManifestResourceStream("MediaBrowser.Dlna.Images." + filename.ToLower())
};
}
private User GetUser(DeviceProfile profile)
{
if (!string.IsNullOrEmpty(profile.UserId))
{
var user = _userManager.GetUserById(new Guid(profile.UserId));
if (user != null)
{
return user;
}
}
if (!string.IsNullOrEmpty(_config.Configuration.DlnaOptions.DefaultUserId))
{
var user = _userManager.GetUserById(new Guid(_config.Configuration.DlnaOptions.DefaultUserId));
if (user != null)
{
return user;
}
}
// No configuration so it's going to be pretty arbitrary
return _userManager.Users.First();
}
}
}

View File

@ -0,0 +1,171 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MediaBrowser.Dlna.Eventing
{
public class EventManager : IEventManager
{
private readonly ConcurrentDictionary<string, EventSubscription> _subscriptions =
new ConcurrentDictionary<string, EventSubscription>(StringComparer.OrdinalIgnoreCase);
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
public EventManager(ILogManager logManager, IHttpClient httpClient)
{
_httpClient = httpClient;
_logger = logManager.GetLogger("DlnaEventManager");
}
public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, int? timeoutSeconds)
{
var timeout = timeoutSeconds ?? 300;
var subscription = GetSubscription(subscriptionId, true);
_logger.Debug("Renewing event subscription for {0} with timeout of {1} to {2}",
subscription.NotificationType,
timeout,
subscription.CallbackUrl);
subscription.TimeoutSeconds = timeout;
subscription.SubscriptionTime = DateTime.UtcNow;
return GetEventSubscriptionResponse(subscriptionId, timeout);
}
public EventSubscriptionResponse CreateEventSubscription(string notificationType, int? timeoutSeconds, string callbackUrl)
{
var timeout = timeoutSeconds ?? 300;
var id = Guid.NewGuid().ToString("N");
_logger.Debug("Creating event subscription for {0} with timeout of {1} to {2}",
notificationType,
timeout,
callbackUrl);
_subscriptions.TryAdd(id, new EventSubscription
{
Id = id,
CallbackUrl = callbackUrl,
SubscriptionTime = DateTime.UtcNow,
TimeoutSeconds = timeout
});
return GetEventSubscriptionResponse(id, timeout);
}
public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)
{
_logger.Debug("Cancelling event subscription {0}", subscriptionId);
EventSubscription sub;
_subscriptions.TryRemove(subscriptionId, out sub);
return new EventSubscriptionResponse
{
Content = "\r\n",
ContentType = "text/plain"
};
}
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, int timeoutSeconds)
{
var response = new EventSubscriptionResponse
{
Content = "\r\n",
ContentType = "text/plain"
};
response.Headers["SID"] = "uuid:" + subscriptionId;
response.Headers["TIMEOUT"] = "SECOND-" + timeoutSeconds.ToString(_usCulture);
return response;
}
public EventSubscription GetSubscription(string id)
{
return GetSubscription(id, false);
}
private EventSubscription GetSubscription(string id, bool throwOnMissing)
{
EventSubscription e;
if (!_subscriptions.TryGetValue(id, out e) && throwOnMissing)
{
throw new ResourceNotFoundException("Event with Id " + id + " not found.");
}
return e;
}
public Task TriggerEvent(string notificationType, IDictionary<string, string> stateVariables)
{
var subs = _subscriptions.Values
.Where(i => !i.IsExpired && string.Equals(notificationType, i.NotificationType, StringComparison.OrdinalIgnoreCase))
.ToList();
var tasks = subs.Select(i => TriggerEvent(i, stateVariables));
return Task.WhenAll(tasks);
}
private async Task TriggerEvent(EventSubscription subscription, IDictionary<string, string> stateVariables)
{
var builder = new StringBuilder();
builder.Append("<?xml version=\"1.0\"?>");
builder.Append("<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">");
foreach (var key in stateVariables.Keys)
{
builder.Append("<e:property>");
builder.Append("<" + key + ">");
builder.Append(stateVariables[key]);
builder.Append("</" + key + ">");
builder.Append("</e:property>");
}
builder.Append("</e:propertyset>");
var options = new HttpRequestOptions
{
RequestContent = builder.ToString(),
RequestContentType = "text/xml",
Url = subscription.CallbackUrl
};
options.RequestHeaders.Add("NT", subscription.NotificationType);
options.RequestHeaders.Add("NTS", "upnp:propchange");
options.RequestHeaders.Add("SID", "uuid:" + subscription.Id);
options.RequestHeaders.Add("SEQ", subscription.TriggerCount.ToString(_usCulture));
try
{
await _httpClient.SendAsync(options, "NOTIFY").ConfigureAwait(false);
}
catch (OperationCanceledException)
{
throw;
}
catch
{
// Already logged at lower levels
}
finally
{
subscription.IncrementTriggerCount();
}
}
}
}

View File

@ -53,6 +53,7 @@
</Compile>
<Compile Include="DlnaManager.cs" />
<Compile Include="Common\Argument.cs" />
<Compile Include="Eventing\EventManager.cs" />
<Compile Include="PlayTo\CurrentIdEventArgs.cs" />
<Compile Include="PlayTo\Device.cs">
<SubType>Code</SubType>
@ -73,6 +74,7 @@
<Compile Include="Profiles\Windows81Profile.cs" />
<Compile Include="Profiles\WindowsMediaCenterProfile.cs" />
<Compile Include="Profiles\WindowsPhoneProfile.cs" />
<Compile Include="Server\ContentDirectory.cs" />
<Compile Include="Server\ControlHandler.cs" />
<Compile Include="Server\ServiceActionListBuilder.cs" />
<Compile Include="Server\ContentDirectoryXmlBuilder.cs" />

View File

@ -24,6 +24,15 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Protocol = "hls",
Container = "ts",
VideoCodec = "h264",
AudioCodec = "aac",
Type = DlnaProfileType.Video,
VideoProfile = "Baseline"
},
new TranscodingProfile
{
Container = "ts",
VideoCodec = "h264",

View File

@ -19,6 +19,15 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Protocol = "hls",
Container = "ts",
VideoCodec = "h264",
AudioCodec = "aac",
Type = DlnaProfileType.Video,
VideoProfile = "Baseline"
},
new TranscodingProfile
{
Container = "mp4",
VideoCodec = "h264",

View File

@ -0,0 +1,151 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
namespace MediaBrowser.Dlna.Server
{
public class ContentDirectory : IContentDirectory, IDisposable
{
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
private readonly IUserDataManager _userDataManager;
private readonly IDlnaManager _dlna;
private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager;
private readonly IEventManager _eventManager;
private int _systemUpdateId;
private Timer _systemUpdateTimer;
public ContentDirectory(IDlnaManager dlna,
IUserDataManager userDataManager,
IImageProcessor imageProcessor,
IDtoService dtoService,
ILibraryManager libraryManager,
ILogManager logManager,
IServerConfigurationManager config,
IUserManager userManager,
IEventManager eventManager)
{
_dlna = dlna;
_userDataManager = userDataManager;
_imageProcessor = imageProcessor;
_dtoService = dtoService;
_libraryManager = libraryManager;
_config = config;
_userManager = userManager;
_eventManager = eventManager;
_logger = logManager.GetLogger("DlnaContentDirectory");
_systemUpdateTimer = new Timer(SystemUdpateTimerCallback, null, Timeout.Infinite,
Convert.ToInt64(TimeSpan.FromMinutes(60).TotalMilliseconds));
}
public string GetContentDirectoryXml(IDictionary<string, string> headers)
{
var profile = _dlna.GetProfile(headers) ??
_dlna.GetDefaultProfile();
return new ContentDirectoryXmlBuilder(profile).GetXml();
}
public ControlResponse ProcessControlRequest(ControlRequest request)
{
var profile = _dlna.GetProfile(request.Headers) ??
_dlna.GetDefaultProfile();
var device = DlnaServerEntryPoint.Instance.GetServerUpnpDevice(request.TargetServerUuId);
var serverAddress = device.Descriptor.ToString().Substring(0, device.Descriptor.ToString().IndexOf("/dlna", StringComparison.OrdinalIgnoreCase));
var user = GetUser(profile);
return new ControlHandler(
_logger,
_libraryManager,
profile,
serverAddress,
_dtoService,
_imageProcessor,
_userDataManager,
user,
_systemUpdateId)
.ProcessControlRequest(request);
}
private User GetUser(DeviceProfile profile)
{
if (!string.IsNullOrEmpty(profile.UserId))
{
var user = _userManager.GetUserById(new Guid(profile.UserId));
if (user != null)
{
return user;
}
}
if (!string.IsNullOrEmpty(_config.Configuration.DlnaOptions.DefaultUserId))
{
var user = _userManager.GetUserById(new Guid(_config.Configuration.DlnaOptions.DefaultUserId));
if (user != null)
{
return user;
}
}
// No configuration so it's going to be pretty arbitrary
return _userManager.Users.First();
}
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private async void SystemUdpateTimerCallback(object state)
{
var values = new Dictionary<string, string>();
_systemUpdateId++;
values["SystemUpdateID"] = _systemUpdateId.ToString(_usCulture);
try
{
await _eventManager.TriggerEvent("upnp:event", values).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error sending system update notification", ex);
}
}
private readonly object _disposeLock = new object();
public void Dispose()
{
lock (_disposeLock)
{
DisposeUpdateTimer();
}
}
private void DisposeUpdateTimer()
{
if (_systemUpdateTimer != null)
{
_systemUpdateTimer.Dispose();
_systemUpdateTimer = null;
}
}
}
}

View File

@ -42,10 +42,10 @@ namespace MediaBrowser.Dlna.Server
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
private int systemID = 0;
private readonly int _systemUpdateId;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user)
public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId)
{
_logger = logger;
_libraryManager = libraryManager;
@ -55,6 +55,7 @@ namespace MediaBrowser.Dlna.Server
_imageProcessor = imageProcessor;
_userDataManager = userDataManager;
_user = user;
_systemUpdateId = systemUpdateId;
}
public ControlResponse ProcessControlRequest(ControlRequest request)
@ -205,7 +206,7 @@ namespace MediaBrowser.Dlna.Server
private IEnumerable<KeyValuePair<string, string>> HandleGetSystemUpdateID()
{
return new Headers { { "Id", systemID.ToString(_usCulture) } };
return new Headers { { "Id", _systemUpdateId.ToString(_usCulture) } };
}
private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList()
@ -308,7 +309,7 @@ namespace MediaBrowser.Dlna.Server
new KeyValuePair<string,string>("Result", resXML),
new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
new KeyValuePair<string,string>("UpdateID", systemID.ToString(_usCulture))
new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
};
}
@ -382,7 +383,7 @@ namespace MediaBrowser.Dlna.Server
new KeyValuePair<string,string>("Result", resXML),
new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
new KeyValuePair<string,string>("UpdateID", systemID.ToString(_usCulture))
new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
};
}

View File

@ -31,7 +31,7 @@ namespace MediaBrowser.Dlna.Server
var builder = new StringBuilder();
builder.Append("<?xml version=\"1.0\"?>");
builder.Append("<root xmlns=\"urn:schemas-upnp-org:device-1-0\" xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\" xmlns:sec=\"http://www.sec.co.kr/dlna\">");
builder.Append("<root xmlns=\"urn:schemas-upnp-org:device-1-0\" xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">");
builder.Append("<specVersion>");
builder.Append("<major>1</major>");
@ -59,16 +59,8 @@ namespace MediaBrowser.Dlna.Server
{
builder.Append("<UDN>uuid:" + SecurityElement.Escape(_serverUdn) + "</UDN>");
builder.Append("<dlna:X_DLNACAP>" + SecurityElement.Escape(_profile.XDlnaCap ?? string.Empty) + "</dlna:X_DLNACAP>");
if (!string.IsNullOrWhiteSpace(_profile.XDlnaDoc))
{
builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" +
SecurityElement.Escape(_profile.XDlnaDoc) + "</dlna:X_DLNADOC>");
}
else
{
builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">DMS-1.50</dlna:X_DLNADOC>");
}
builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" + SecurityElement.Escape(_profile.XDlnaDoc ?? string.Empty) + "</dlna:X_DLNADOC>");
builder.Append("<friendlyName>" + SecurityElement.Escape(_profile.FriendlyName ?? string.Empty) + "</friendlyName>");
builder.Append("<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
@ -80,9 +72,6 @@ namespace MediaBrowser.Dlna.Server
builder.Append("<modelURL>" + SecurityElement.Escape(_profile.ModelUrl ?? string.Empty) + "</modelURL>");
builder.Append("<serialNumber>" + SecurityElement.Escape(_profile.SerialNumber ?? string.Empty) + "</serialNumber>");
builder.Append("<sec:ProductCap>DCM10,getMediaInfo.sec</sec:ProductCap>");
builder.Append("<sec:X_ProductCap>DCM10,getMediaInfo.sec</sec:X_ProductCap>");
if (!string.IsNullOrWhiteSpace(_profile.SonyAggregationFlags))
{
builder.Append("<av:aggregationFlags xmlns:av=\"urn:schemas-sony-com:av\">" + SecurityElement.Escape(_profile.SonyAggregationFlags) + "</av:aggregationFlags>");

View File

@ -117,7 +117,6 @@ namespace MediaBrowser.Dlna.Server
if (_config.Configuration.DlnaOptions.EnableDebugLogging)
{
_logger.Debug("{0} - Datagram method: {1}", endpoint, method);
//_logger.Debug(headers);
}
if (string.Equals(method, "M-SEARCH", StringComparison.OrdinalIgnoreCase))
@ -234,7 +233,10 @@ namespace MediaBrowser.Dlna.Server
private void NotifyAll()
{
_logger.Debug("Sending alive notifications");
if (_config.Configuration.DlnaOptions.EnableDebugLogging)
{
_logger.Debug("Sending alive notifications");
}
foreach (var d in Devices)
{
NotifyDevice(d, "alive", false);
@ -243,7 +245,6 @@ namespace MediaBrowser.Dlna.Server
private void NotifyDevice(UpnpDevice dev, string type, bool sticky)
{
_logger.Debug("NotifyDevice");
var builder = new StringBuilder();
const string argFormat = "{0}: {1}\r\n";
@ -258,7 +259,11 @@ namespace MediaBrowser.Dlna.Server
builder.AppendFormat(argFormat, "USN", dev.USN);
builder.Append("\r\n");
_logger.Debug("{0} said {1}", dev.USN, type);
if (_config.Configuration.DlnaOptions.EnableDebugLogging)
{
_logger.Debug("{0} said {1}", dev.USN, type);
}
SendDatagram(_ssdpEndp, dev.Address, builder.ToString(), sticky);
}

View File

@ -122,6 +122,9 @@
<Compile Include="..\MediaBrowser.Model\Dlna\DlnaMaps.cs">
<Link>Dlna\DlnaMaps.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\EventSubscription.cs">
<Link>Dlna\EventSubscription.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\Filter.cs">
<Link>Dlna\Filter.cs</Link>
</Compile>

View File

@ -109,6 +109,9 @@
<Compile Include="..\MediaBrowser.Model\Dlna\DlnaMaps.cs">
<Link>Dlna\DlnaMaps.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\EventSubscription.cs">
<Link>Dlna\EventSubscription.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\Filter.cs">
<Link>Dlna\Filter.cs</Link>
</Compile>

View File

@ -0,0 +1,34 @@
using System;
namespace MediaBrowser.Model.Dlna
{
public class EventSubscription
{
public string Id { get; set; }
public string CallbackUrl { get; set; }
public string NotificationType { get; set; }
public DateTime SubscriptionTime { get; set; }
public int TimeoutSeconds { get; set; }
public long TriggerCount { get; set; }
public void IncrementTriggerCount()
{
if (TriggerCount == long.MaxValue)
{
TriggerCount = 0;
}
TriggerCount++;
}
public bool IsExpired
{
get
{
return SubscriptionTime.AddSeconds(TimeoutSeconds) >= DateTime.UtcNow;
}
}
}
}

View File

@ -73,6 +73,7 @@
<Compile Include="Dlna\DeviceProfileInfo.cs" />
<Compile Include="Dlna\DirectPlayProfile.cs" />
<Compile Include="Dlna\DlnaMaps.cs" />
<Compile Include="Dlna\EventSubscription.cs" />
<Compile Include="Dlna\Filter.cs" />
<Compile Include="Dlna\MediaFormatProfile.cs" />
<Compile Include="Dlna\MediaFormatProfileResolver.cs" />

View File

@ -32,7 +32,9 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Controller.Themes;
using MediaBrowser.Dlna;
using MediaBrowser.Dlna.Eventing;
using MediaBrowser.Dlna.PlayTo;
using MediaBrowser.Dlna.Server;
using MediaBrowser.MediaEncoding.BdInfo;
using MediaBrowser.MediaEncoding.Encoder;
using MediaBrowser.Model.Logging;
@ -506,9 +508,15 @@ namespace MediaBrowser.ServerApplication
var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger);
RegisterSingleInstance<IAppThemeManager>(appThemeManager);
var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("DLNA"), JsonSerializer, UserManager, LibraryManager, DtoService, ImageProcessor, UserDataManager, ServerConfigurationManager);
var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("Dlna"), JsonSerializer);
RegisterSingleInstance<IDlnaManager>(dlnaManager);
var dlnaEventManager = new EventManager(LogManager, HttpClient);
RegisterSingleInstance<IEventManager>(dlnaEventManager);
var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, DtoService, LibraryManager, LogManager, ServerConfigurationManager, UserManager, dlnaEventManager);
RegisterSingleInstance<IContentDirectory>(contentDirectory);
var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor);
RegisterSingleInstance<ICollectionManager>(collectionManager);