aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/Dlna/DlnaServerService.cs47
-rw-r--r--MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs2
-rw-r--r--MediaBrowser.Common/Net/IHttpClient.cs8
-rw-r--r--MediaBrowser.Controller/Dlna/EventSubscriptionResponse.cs17
-rw-r--r--MediaBrowser.Controller/Dlna/IContentDirectory.cs21
-rw-r--r--MediaBrowser.Controller/Dlna/IDlnaManager.cs14
-rw-r--r--MediaBrowser.Controller/Dlna/IEventManager.cs47
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj3
-rw-r--r--MediaBrowser.Dlna/DlnaManager.cs87
-rw-r--r--MediaBrowser.Dlna/Eventing/EventManager.cs171
-rw-r--r--MediaBrowser.Dlna/MediaBrowser.Dlna.csproj2
-rw-r--r--MediaBrowser.Dlna/Profiles/Windows81Profile.cs9
-rw-r--r--MediaBrowser.Dlna/Profiles/WindowsPhoneProfile.cs9
-rw-r--r--MediaBrowser.Dlna/Server/ContentDirectory.cs151
-rw-r--r--MediaBrowser.Dlna/Server/ControlHandler.cs11
-rw-r--r--MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs17
-rw-r--r--MediaBrowser.Dlna/Server/SsdpHandler.cs13
-rw-r--r--MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj3
-rw-r--r--MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj3
-rw-r--r--MediaBrowser.Model/Dlna/EventSubscription.cs34
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj1
-rw-r--r--MediaBrowser.ServerApplication/ApplicationHost.cs10
22 files changed, 528 insertions, 152 deletions
diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs
index 3ae9ddc63..b357409c3 100644
--- a/MediaBrowser.Api/Dlna/DlnaServerService.cs
+++ b/MediaBrowser.Api/Dlna/DlnaServerService.cs
@@ -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");
diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
index 96c207b7b..69533ef9d 100644
--- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
@@ -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);
diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs
index e583c6b26..4eabbc803 100644
--- a/MediaBrowser.Common/Net/IHttpClient.cs
+++ b/MediaBrowser.Common/Net/IHttpClient.cs
@@ -44,6 +44,14 @@ namespace MediaBrowser.Common.Net
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>
/// <param name="url">The URL.</param>
diff --git a/MediaBrowser.Controller/Dlna/EventSubscriptionResponse.cs b/MediaBrowser.Controller/Dlna/EventSubscriptionResponse.cs
new file mode 100644
index 000000000..8b551c2a7
--- /dev/null
+++ b/MediaBrowser.Controller/Dlna/EventSubscriptionResponse.cs
@@ -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>();
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Dlna/IContentDirectory.cs b/MediaBrowser.Controller/Dlna/IContentDirectory.cs
new file mode 100644
index 000000000..e48d498df
--- /dev/null
+++ b/MediaBrowser.Controller/Dlna/IContentDirectory.cs
@@ -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);
+ }
+}
diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs
index e9e2aae54..b7a06b368 100644
--- a/MediaBrowser.Controller/Dlna/IDlnaManager.cs
+++ b/MediaBrowser.Controller/Dlna/IDlnaManager.cs
@@ -65,20 +65,6 @@ namespace MediaBrowser.Controller.Dlna
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>
/// <param name="filename">The filename.</param>
diff --git a/MediaBrowser.Controller/Dlna/IEventManager.cs b/MediaBrowser.Controller/Dlna/IEventManager.cs
new file mode 100644
index 000000000..4abf623a9
--- /dev/null
+++ b/MediaBrowser.Controller/Dlna/IEventManager.cs
@@ -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);
+ }
+}
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 235c3c1e5..cc3f3d08b 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -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" />
diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs
index 0a1a53435..963c68d9a 100644
--- a/MediaBrowser.Dlna/DlnaManager.cs
+++ b/MediaBrowser.Dlna/DlnaManager.cs
@@ -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();
- }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Eventing/EventManager.cs b/MediaBrowser.Dlna/Eventing/EventManager.cs
new file mode 100644
index 000000000..3961c366a
--- /dev/null
+++ b/MediaBrowser.Dlna/Eventing/EventManager.cs
@@ -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();
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
index 016cac4d6..81ddf7283 100644
--- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
+++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
@@ -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" />
diff --git a/MediaBrowser.Dlna/Profiles/Windows81Profile.cs b/MediaBrowser.Dlna/Profiles/Windows81Profile.cs
index 000e07b0a..5e74884a2 100644
--- a/MediaBrowser.Dlna/Profiles/Windows81Profile.cs
+++ b/MediaBrowser.Dlna/Profiles/Windows81Profile.cs
@@ -25,6 +25,15 @@ namespace MediaBrowser.Dlna.Profiles
},
new TranscodingProfile
{
+ Protocol = "hls",
+ Container = "ts",
+ VideoCodec = "h264",
+ AudioCodec = "aac",
+ Type = DlnaProfileType.Video,
+ VideoProfile = "Baseline"
+ },
+ new TranscodingProfile
+ {
Container = "ts",
VideoCodec = "h264",
AudioCodec = "aac",
diff --git a/MediaBrowser.Dlna/Profiles/WindowsPhoneProfile.cs b/MediaBrowser.Dlna/Profiles/WindowsPhoneProfile.cs
index 6ed6abc76..80cc8ad71 100644
--- a/MediaBrowser.Dlna/Profiles/WindowsPhoneProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/WindowsPhoneProfile.cs
@@ -20,6 +20,15 @@ namespace MediaBrowser.Dlna.Profiles
},
new TranscodingProfile
{
+ Protocol = "hls",
+ Container = "ts",
+ VideoCodec = "h264",
+ AudioCodec = "aac",
+ Type = DlnaProfileType.Video,
+ VideoProfile = "Baseline"
+ },
+ new TranscodingProfile
+ {
Container = "mp4",
VideoCodec = "h264",
AudioCodec = "aac",
diff --git a/MediaBrowser.Dlna/Server/ContentDirectory.cs b/MediaBrowser.Dlna/Server/ContentDirectory.cs
new file mode 100644
index 000000000..c5b336090
--- /dev/null
+++ b/MediaBrowser.Dlna/Server/ContentDirectory.cs
@@ -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;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/Server/ControlHandler.cs b/MediaBrowser.Dlna/Server/ControlHandler.cs
index f97fdc88e..88c0349db 100644
--- a/MediaBrowser.Dlna/Server/ControlHandler.cs
+++ b/MediaBrowser.Dlna/Server/ControlHandler.cs
@@ -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))
};
}
diff --git a/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs b/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs
index 07552cd3e..4b66e489f 100644
--- a/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs
+++ b/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs
@@ -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>");
diff --git a/MediaBrowser.Dlna/Server/SsdpHandler.cs b/MediaBrowser.Dlna/Server/SsdpHandler.cs
index 236078df0..0430f6a02 100644
--- a/MediaBrowser.Dlna/Server/SsdpHandler.cs
+++ b/MediaBrowser.Dlna/Server/SsdpHandler.cs
@@ -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);
}
diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
index 0df8f2b7f..fe2fe2091 100644
--- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
+++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
@@ -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>
diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
index 570ddaa31..b833a19a7 100644
--- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
+++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
@@ -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>
diff --git a/MediaBrowser.Model/Dlna/EventSubscription.cs b/MediaBrowser.Model/Dlna/EventSubscription.cs
new file mode 100644
index 000000000..863ea508a
--- /dev/null
+++ b/MediaBrowser.Model/Dlna/EventSubscription.cs
@@ -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;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index a4f0a609f..c18d7e8dc 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -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" />
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 30aeaf877..8ee060c3c 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -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);