Browse Source

dlna server fixes

Luke Pulverenti 11 years ago
parent
commit
247400717e
42 changed files with 575 additions and 127 deletions
  1. 88 8
      MediaBrowser.Api/Dlna/DlnaServerService.cs
  2. 39 9
      MediaBrowser.Dlna/DlnaManager.cs
  3. 248 78
      MediaBrowser.Dlna/Server/ControlHandler.cs
  4. 4 3
      MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs
  5. 12 0
      MediaBrowser.Dlna/Server/Headers.cs
  6. 37 22
      MediaBrowser.Dlna/Server/SsdpHandler.cs
  7. 2 2
      MediaBrowser.Dlna/Server/UpnpDevice.cs
  8. 1 1
      MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
  9. 9 0
      MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
  10. 9 0
      MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
  11. 5 0
      MediaBrowser.Model/Configuration/DlnaOptions.cs
  12. 2 0
      MediaBrowser.Model/Dlna/DeviceProfile.cs
  13. 26 0
      MediaBrowser.Model/Dlna/Filter.cs
  14. 49 0
      MediaBrowser.Model/Dlna/SearchCriteria.cs
  15. 11 0
      MediaBrowser.Model/Dlna/SortCriteria.cs
  16. 3 0
      MediaBrowser.Model/MediaBrowser.Model.csproj
  17. 10 1
      MediaBrowser.Providers/Movies/MovieXmlProvider.cs
  18. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/ar.json
  19. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/ca.json
  20. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/cs.json
  21. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/de.json
  22. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/el.json
  23. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/en_GB.json
  24. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/en_US.json
  25. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/es.json
  26. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/es_MX.json
  27. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/fr.json
  28. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/he.json
  29. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/it.json
  30. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/kk.json
  31. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/ms.json
  32. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/nb.json
  33. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/nl.json
  34. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json
  35. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json
  36. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/ru.json
  37. 12 2
      MediaBrowser.Server.Implementations/Localization/Server/server.json
  38. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/sv.json
  39. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json
  40. 1 1
      MediaBrowser.ServerApplication/ApplicationHost.cs
  41. 1 0
      MediaBrowser.WebDashboard/Api/DashboardService.cs
  42. 6 0
      MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

+ 88 - 8
MediaBrowser.Api/Dlna/DlnaServerService.cs

@@ -1,10 +1,12 @@
-using System;
-using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Controller.Dlna;
 using ServiceStack;
 using ServiceStack.Text.Controller;
 using ServiceStack.Web;
+using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
+using System.Linq;
 using System.Threading.Tasks;
 
 namespace MediaBrowser.Api.Dlna
@@ -17,21 +19,28 @@ namespace MediaBrowser.Api.Dlna
         public string UuId { get; set; }
     }
 
-    [Route("/Dlna/contentdirectory.xml", "GET", Summary = "Gets dlna content directory xml")]
-    [Route("/Dlna/contentdirectory", "GET", Summary = "Gets dlna content directory xml")]
+    [Route("/Dlna/contentdirectory/contentdirectory.xml", "GET", Summary = "Gets dlna content directory xml")]
+    [Route("/Dlna/contentdirectory/contentdirectory", "GET", Summary = "Gets dlna content directory xml")]
     public class GetContentDirectory
     {
     }
 
-    [Route("/Dlna/{UuId}/control", "POST", Summary = "Processes a control request")]
+    [Route("/Dlna/contentdirectory/{UuId}/control", "POST", Summary = "Processes a control request")]
     public class ProcessControlRequest : IRequiresRequestStream
     {
         [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
         public string UuId { get; set; }
-        
+
         public Stream RequestStream { get; set; }
     }
 
+    [Route("/Dlna/contentdirectory/{UuId}/events", Summary = "Processes an event subscription request")]
+    public class ProcessEventRequest
+    {
+        [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+        public string UuId { get; set; }
+    }
+
     [Route("/Dlna/icons/{Filename}", "GET", Summary = "Gets a server icon")]
     public class GetIcon
     {
@@ -72,8 +81,8 @@ namespace MediaBrowser.Api.Dlna
         private async Task<ControlResponse> PostAsync(ProcessControlRequest request)
         {
             var pathInfo = PathInfo.Parse(Request.PathInfo);
-            var id = pathInfo.GetArgumentValue<string>(1);
-            
+            var id = pathInfo.GetArgumentValue<string>(2);
+
             using (var reader = new StreamReader(request.RequestStream))
             {
                 return _dlnaManager.ProcessControlRequest(new ControlRequest
@@ -111,5 +120,76 @@ namespace MediaBrowser.Api.Dlna
                 }
             }
         }
+
+        public object Any(ProcessEventRequest request)
+        {
+            var subscriptionId = GetHeader("SID");
+            var notificationType = GetHeader("NT");
+            var callback = GetHeader("CALLBACK");
+            var timeoutString = GetHeader("TIMEOUT");
+
+            var timeout = ParseTimeout(timeoutString) ?? 300;
+
+            if (string.Equals(Request.Verb, "SUBSCRIBE", StringComparison.OrdinalIgnoreCase))
+            {
+                if (string.IsNullOrEmpty(notificationType))
+                {
+                    RenewEvent(subscriptionId, timeout);
+                }
+                else
+                {
+                    SubscribeToEvent(notificationType, timeout, callback);
+                }
+
+                return GetSubscriptionResponse(request.UuId, timeout);
+            }
+
+            UnsubscribeFromEvent(subscriptionId);
+            return ResultFactory.GetResult("", "text/plain");
+        }
+
+        private void UnsubscribeFromEvent(string subscriptionId)
+        {
+
+        }
+
+        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);
+        }
+
+        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+        private int? ParseTimeout(string header)
+        {
+            if (!string.IsNullOrEmpty(header))
+            {
+                // Starts with SECOND-
+                header = header.Split('-').Last();
+
+                int val;
+
+                if (int.TryParse(header, NumberStyles.Any, _usCulture, out val))
+                {
+                    return val;
+                }
+            }
+
+            return null;
+        }
     }
 }

+ 39 - 9
MediaBrowser.Dlna/DlnaManager.cs

@@ -1,9 +1,11 @@
 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;
@@ -31,8 +33,9 @@ namespace MediaBrowser.Dlna
         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)
+        public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IApplicationPaths appPaths, ILogger logger, IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager, IServerConfigurationManager config)
         {
             _xmlSerializer = xmlSerializer;
             _fileSystem = fileSystem;
@@ -44,6 +47,7 @@ namespace MediaBrowser.Dlna
             _dtoService = dtoService;
             _imageProcessor = imageProcessor;
             _userDataManager = userDataManager;
+            _config = config;
 
             //DumpProfiles();
         }
@@ -451,15 +455,11 @@ namespace MediaBrowser.Dlna
 
             var current = GetProfileInfosInternal().First(i => string.Equals(i.Info.Id, profile.Id, StringComparison.OrdinalIgnoreCase));
 
-            if (current.Info.Type == DeviceProfileType.System)
-            {
-                throw new ArgumentException("System profiles are readonly");
-            }
-
             var newFilename = _fileSystem.GetValidFilename(profile.Name) + ".xml";
             var path = Path.Combine(UserProfilesPath, newFilename);
 
-            if (!string.Equals(path, current.Path, StringComparison.Ordinal))
+            if (!string.Equals(path, current.Path, StringComparison.Ordinal) &&
+                current.Info.Type != DeviceProfileType.System)
             {
                 File.Delete(current.Path);
             }
@@ -516,15 +516,17 @@ namespace MediaBrowser.Dlna
 
             var serverAddress = device.Descriptor.ToString().Substring(0, device.Descriptor.ToString().IndexOf("/dlna", StringComparison.OrdinalIgnoreCase));
 
+            var user = GetUser(profile);
+
             return new ControlHandler(
                 _logger, 
-                _userManager, 
                 _libraryManager, 
                 profile, 
                 serverAddress, 
                 _dtoService, 
                 _imageProcessor, 
-                _userDataManager)
+                _userDataManager,
+                user)
                 .ProcessControlRequest(request);
         }
 
@@ -540,5 +542,33 @@ 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();
+        }
     }
 }

+ 248 - 78
MediaBrowser.Dlna/Server/ControlHandler.cs

@@ -25,12 +25,12 @@ namespace MediaBrowser.Dlna.Server
     public class ControlHandler
     {
         private readonly ILogger _logger;
-        private readonly IUserManager _userManager;
         private readonly ILibraryManager _libraryManager;
         private readonly DeviceProfile _profile;
         private readonly IDtoService _dtoService;
         private readonly IImageProcessor _imageProcessor;
         private readonly IUserDataManager _userDataManager;
+        private readonly User _user;
 
         private readonly string _serverAddress;
 
@@ -44,16 +44,16 @@ namespace MediaBrowser.Dlna.Server
         private int systemID = 0;
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
-        public ControlHandler(ILogger logger, IUserManager userManager, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager)
+        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user)
         {
             _logger = logger;
-            _userManager = userManager;
             _libraryManager = libraryManager;
             _profile = profile;
             _serverAddress = serverAddress;
             _dtoService = dtoService;
             _imageProcessor = imageProcessor;
             _userDataManager = userDataManager;
+            _user = user;
         }
 
         public ControlResponse ProcessControlRequest(ControlRequest request)
@@ -104,31 +104,24 @@ namespace MediaBrowser.Dlna.Server
 
             _logger.Debug("Received control request {0}", method.Name);
 
-            var user = _userManager.Users.First();
-
-            switch (method.LocalName)
-            {
-                case "GetSearchCapabilities":
-                    result = HandleGetSearchCapabilities();
-                    break;
-                case "GetSortCapabilities":
-                    result = HandleGetSortCapabilities();
-                    break;
-                case "GetSystemUpdateID":
-                    result = HandleGetSystemUpdateID();
-                    break;
-                case "Browse":
-                    result = HandleBrowse(sparams, user, deviceId);
-                    break;
-                case "X_GetFeatureList":
-                    result = HandleXGetFeatureList();
-                    break;
-                case "X_SetBookmark":
-                    result = HandleXSetBookmark(sparams, user);
-                    break;
-                default:
-                    throw new ResourceNotFoundException();
-            }
+            var user = _user;
+
+            if (string.Equals(method.LocalName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase))
+                result = HandleGetSearchCapabilities();
+            else if (string.Equals(method.LocalName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase))
+                result = HandleGetSortCapabilities();
+            else if (string.Equals(method.LocalName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase))
+                result = HandleGetSystemUpdateID();
+            else if (string.Equals(method.LocalName, "Browse", StringComparison.OrdinalIgnoreCase))
+                result = HandleBrowse(sparams, user, deviceId);
+            else if (string.Equals(method.LocalName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
+                result = HandleXGetFeatureList();
+            else if (string.Equals(method.LocalName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase))
+                result = HandleXSetBookmark(sparams, user);
+            else if (string.Equals(method.LocalName, "Search", StringComparison.OrdinalIgnoreCase))
+                result = HandleSearch(sparams, user, deviceId);
+            else
+                throw new ResourceNotFoundException("Unexpected control request name: " + method.LocalName);
 
             var response = env.CreateElement(String.Format("u:{0}Response", method.LocalName), method.NamespaceURI);
             rbody.AppendChild(response);
@@ -241,10 +234,12 @@ namespace MediaBrowser.Dlna.Server
         {
             var id = sparams["ObjectID"];
             var flag = sparams["BrowseFlag"];
+            var filter = new Filter(sparams.GetValueOrDefault("Filter", "*"));
+            var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", ""));
 
             var provided = 0;
-            int requested = 0;
-            int start = 0;
+            var requested = 0;
+            var start = 0;
 
             if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requested) && requested <= 0)
             {
@@ -267,11 +262,13 @@ namespace MediaBrowser.Dlna.Server
 
             var folder = (Folder)GetItemFromObjectId(id, user);
 
-            var children = GetChildrenSorted(folder, user).ToList();
+            var children = GetChildrenSorted(folder, user, sortCriteria).ToList();
 
+            var totalCount = children.Count;
+            
             if (string.Equals(flag, "BrowseMetadata"))
             {
-                Browse_AddFolder(result, folder, children.Count);
+                Browse_AddFolder(result, folder, children.Count, filter);
                 provided++;
             }
             else
@@ -292,13 +289,13 @@ namespace MediaBrowser.Dlna.Server
                     if (i.IsFolder)
                     {
                         var f = (Folder)i;
-                        var childCount = GetChildrenSorted(f, user).Count();
+                        var childCount = GetChildrenSorted(f, user, sortCriteria).Count();
 
-                        Browse_AddFolder(result, f, childCount);
+                        Browse_AddFolder(result, f, childCount, filter);
                     }
                     else
                     {
-                        Browse_AddItem(result, i, user, deviceId);
+                        Browse_AddItem(result, i, user, deviceId, filter);
                     }
                 }
             }
@@ -309,35 +306,175 @@ namespace MediaBrowser.Dlna.Server
             {
                 new KeyValuePair<string,string>("Result", resXML),
                 new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
-                new KeyValuePair<string,string>("TotalMatches", children.Count.ToString(_usCulture)),
+                new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
                 new KeyValuePair<string,string>("UpdateID", systemID.ToString(_usCulture))
             };
         }
 
-        private IEnumerable<BaseItem> GetChildrenSorted(Folder folder, User user)
+        private IEnumerable<KeyValuePair<string, string>> HandleSearch(Headers sparams, User user, string deviceId)
         {
-            var children = folder.GetChildren(user, true).Where(i => i.LocationType != LocationType.Virtual);
+            var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", ""));
+            var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", ""));
+            var filter = new Filter(sparams.GetValueOrDefault("Filter", "*"));
+
+            // sort example: dc:title, dc:date
+
+            var provided = 0;
+            var requested = 0;
+            var start = 0;
+
+            if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requested) && requested <= 0)
+            {
+                requested = 0;
+            }
+            if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out start) && start <= 0)
+            {
+                start = 0;
+            }
+
+            //var root = GetItem(id) as IMediaFolder;
+            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);
+            didl.SetAttribute("xmlns:sec", NS_SEC);
+            result.AppendChild(didl);
+
+            var folder = (Folder)GetItemFromObjectId(sparams["ContainerID"], user);
+
+            var children = GetChildrenSorted(folder, user, searchCriteria, sortCriteria).ToList();
+
+            var totalCount = children.Count;
+
+            if (start > 0)
+            {
+                children = children.Skip(start).ToList();
+            }
+            if (requested > 0)
+            {
+                children = children.Take(requested).ToList();
+            }
+
+            provided = children.Count;
+
+            foreach (var i in children)
+            {
+                if (i.IsFolder)
+                {
+                    var f = (Folder)i;
+                    var childCount = GetChildrenSorted(f, user, searchCriteria, sortCriteria).Count();
+
+                    Browse_AddFolder(result, f, childCount, filter);
+                }
+                else
+                {
+                    Browse_AddItem(result, i, user, deviceId, filter);
+                }
+            }
+
+            var resXML = result.OuterXml;
+
+            return new List<KeyValuePair<string, string>>
+            {
+                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))
+            };
+        }
+
+        private IEnumerable<BaseItem> GetChildrenSorted(Folder folder, User user, SearchCriteria search, SortCriteria sort)
+        {
+            if (search.SearchType == SearchType.Unknown)
+            {
+                return GetChildrenSorted(folder, user, sort);
+            }
+
+            var items = folder.GetRecursiveChildren(user);
+            items = FilterUnsupportedContent(items);
+
+            if (search.SearchType == SearchType.Audio)
+            {
+                items = items.OfType<Audio>();
+            }
+            else if (search.SearchType == SearchType.Video)
+            {
+                items = items.OfType<Video>();
+            }
+            else if (search.SearchType == SearchType.Image)
+            {
+                items = items.OfType<Photo>();
+            }
+            else if (search.SearchType == SearchType.Playlist)
+            {
+            }
+
+            return SortItems(items, user, sort);
+        }
+
+        private IEnumerable<BaseItem> GetChildrenSorted(Folder folder, User user, SortCriteria sort)
+        {
+            var items = folder.GetChildren(user, true);
+
+            items = FilterUnsupportedContent(items);
 
             if (folder is Series || folder is Season || folder is BoxSet)
             {
-                return children;
+                return items;
             }
 
-            return _libraryManager.Sort(children, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
+            return SortItems(items, user, sort);
+        }
+
+        private IEnumerable<BaseItem> SortItems(IEnumerable<BaseItem> items, User user, SortCriteria sort)
+        {
+            return _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
+        }
+
+        private IEnumerable<BaseItem> FilterUnsupportedContent(IEnumerable<BaseItem> items)
+        {
+            return items.Where(i =>
+            {
+                // Unplayable
+                // TODO: Display and prevent playback with restricted flag?
+                if (i.LocationType == LocationType.Virtual)
+                {
+                    return false;
+                }
+
+                // Unplayable
+                // TODO: Display and prevent playback with restricted flag?
+                var supportsPlaceHolder = i as ISupportsPlaceHolders;
+                if (supportsPlaceHolder != null && supportsPlaceHolder.IsPlaceHolder)
+                {
+                    return false;
+                }
+
+                // Upnp renderers won't understand these
+                // TODO: Display and prevent playback with restricted flag?
+                if (i is Game || i is Book)
+                {
+                    return false;
+                }
+
+                return true;
+            });
         }
 
         private BaseItem GetItemFromObjectId(string id, User user)
         {
-           return string.IsNullOrWhiteSpace(id) || string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
+            return string.IsNullOrWhiteSpace(id) || string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
 
-                // Samsung sometimes uses 1 as root
-                || string.Equals(id, "1", StringComparison.OrdinalIgnoreCase)
+                 // Samsung sometimes uses 1 as root
+                 || string.Equals(id, "1", StringComparison.OrdinalIgnoreCase)
 
-                ? user.RootFolder
-                : _libraryManager.GetItemById(new Guid(id));
+                 ? user.RootFolder
+                 : _libraryManager.GetItemById(new Guid(id));
         }
 
-        private void Browse_AddFolder(XmlDocument result, Folder f, int childCount)
+        private void Browse_AddFolder(XmlDocument result, Folder f, int childCount, Filter filter)
         {
             var container = result.CreateElement(string.Empty, "container", NS_DIDL);
             container.SetAttribute("restricted", "0");
@@ -355,7 +492,7 @@ namespace MediaBrowser.Dlna.Server
                 container.SetAttribute("parentID", parent.Id.ToString("N"));
             }
 
-            AddCommonFields(f, container);
+            AddCommonFields(f, container, filter);
 
             AddCover(f, container);
 
@@ -377,7 +514,7 @@ namespace MediaBrowser.Dlna.Server
             }
         }
 
-        private void Browse_AddItem(XmlDocument result, BaseItem item, User user, string deviceId)
+        private void Browse_AddItem(XmlDocument result, BaseItem item, User user, string deviceId, Filter filter)
         {
             var element = result.CreateElement(string.Empty, "item", NS_DIDL);
             element.SetAttribute("restricted", "1");
@@ -392,7 +529,7 @@ namespace MediaBrowser.Dlna.Server
 
             AddBookmarkInfo(item, user, element);
 
-            AddGeneralProperties(item, element);
+            AddGeneralProperties(item, element, filter);
 
             // refID?
             // storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
@@ -400,13 +537,13 @@ namespace MediaBrowser.Dlna.Server
             var audio = item as Audio;
             if (audio != null)
             {
-                AddAudioResource(element, audio, deviceId);
+                AddAudioResource(element, audio, deviceId, filter);
             }
 
             var video = item as Video;
             if (video != null)
             {
-                AddVideoResource(element, video, deviceId);
+                AddVideoResource(element, video, deviceId, filter);
             }
 
             AddCover(item, element);
@@ -414,7 +551,7 @@ namespace MediaBrowser.Dlna.Server
             result.DocumentElement.AppendChild(element);
         }
 
-        private void AddVideoResource(XmlElement container, Video video, string deviceId)
+        private void AddVideoResource(XmlElement container, Video video, string deviceId, Filter filter)
         {
             var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
 
@@ -441,13 +578,16 @@ namespace MediaBrowser.Dlna.Server
                 res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
             }
 
-            if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
+            if (filter.Contains("res@size"))
             {
-                var size = streamInfo.TargetSize;
-
-                if (size.HasValue)
+                if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
                 {
-                    res.SetAttribute("size", size.Value.ToString(_usCulture));
+                    var size = streamInfo.TargetSize;
+
+                    if (size.HasValue)
+                    {
+                        res.SetAttribute("size", size.Value.ToString(_usCulture));
+                    }
                 }
             }
 
@@ -473,11 +613,14 @@ namespace MediaBrowser.Dlna.Server
                 res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
             }
 
-            if (targetWidth.HasValue && targetHeight.HasValue)
+            if (filter.Contains("res@resolution"))
             {
-                res.SetAttribute("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
+                if (targetWidth.HasValue && targetHeight.HasValue)
+                {
+                    res.SetAttribute("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
+                }
             }
-            
+
             if (targetSampleRate.HasValue)
             {
                 res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
@@ -514,7 +657,7 @@ namespace MediaBrowser.Dlna.Server
             container.AppendChild(res);
         }
 
-        private void AddAudioResource(XmlElement container, Audio audio, string deviceId)
+        private void AddAudioResource(XmlElement container, Audio audio, string deviceId, Filter filter)
         {
             var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
 
@@ -538,13 +681,16 @@ namespace MediaBrowser.Dlna.Server
                 res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
             }
 
-            if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
+            if (filter.Contains("res@size"))
             {
-                var size = streamInfo.TargetSize;
-
-                if (size.HasValue)
+                if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
                 {
-                    res.SetAttribute("size", size.Value.ToString(_usCulture));
+                    var size = streamInfo.TargetSize;
+
+                    if (size.HasValue)
+                    {
+                        res.SetAttribute("size", size.Value.ToString(_usCulture));
+                    }
                 }
             }
 
@@ -659,13 +805,17 @@ namespace MediaBrowser.Dlna.Server
         /// <summary>
         /// Adds fields used by both items and folders
         /// </summary>
-        /// <param name="item"></param>
-        /// <param name="element"></param>
-        private void AddCommonFields(BaseItem item, XmlElement element)
+        /// <param name="item">The item.</param>
+        /// <param name="element">The element.</param>
+        /// <param name="filter">The filter.</param>
+        private void AddCommonFields(BaseItem item, XmlElement element, Filter filter)
         {
-            if (item.PremiereDate.HasValue)
+            if (filter.Contains("dc:date"))
             {
-                AddValue(element, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
+                if (item.PremiereDate.HasValue)
+                {
+                    AddValue(element, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
+                }
             }
 
             if (item.Genres.Count > 0)
@@ -678,24 +828,44 @@ namespace MediaBrowser.Dlna.Server
                 AddValue(element, "upnp", "publisher", item.Studios[0], NS_UPNP);
             }
 
-            AddValue(element, "dc", "title", item.Name, NS_DC);
+            if (filter.Contains("dc:title"))
+            {
+                AddValue(element, "dc", "title", item.Name, NS_DC);
+            }
 
-            if (!string.IsNullOrWhiteSpace(item.Overview))
+            if (filter.Contains("dc:description"))
             {
-                AddValue(element, "dc", "description", item.Overview, NS_DC);
+                if (!string.IsNullOrWhiteSpace(item.Overview))
+                {
+                    AddValue(element, "dc", "description", item.Overview, NS_DC);
+                }
+            }
+            if (filter.Contains("upnp:longDescription"))
+            {
+                if (!string.IsNullOrWhiteSpace(item.Overview))
+                {
+                    AddValue(element, "upnp", "longDescription", item.Overview, NS_UPNP);
+                }
             }
 
             if (!string.IsNullOrEmpty(item.OfficialRating))
             {
-                AddValue(element, "dc", "rating", item.OfficialRating, NS_DC);
+                if (filter.Contains("dc:rating"))
+                {
+                    AddValue(element, "dc", "rating", item.OfficialRating, NS_DC);
+                }
+                if (filter.Contains("upnp:rating"))
+                {
+                    AddValue(element, "upnp", "rating", item.OfficialRating, NS_UPNP);
+                }
             }
 
             AddPeople(item, element);
         }
 
-        private void AddGeneralProperties(BaseItem item, XmlElement element)
+        private void AddGeneralProperties(BaseItem item, XmlElement element, Filter filter)
         {
-            AddCommonFields(item, element);
+            AddCommonFields(item, element, filter);
 
             var audio = item as Audio;
 
@@ -861,7 +1031,7 @@ namespace MediaBrowser.Dlna.Server
             {
 
             }
-            
+
             return new ImageDownloadInfo
             {
                 ItemId = item.Id.ToString("N"),

+ 4 - 3
MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs

@@ -163,7 +163,7 @@ namespace MediaBrowser.Dlna.Server
                 Height = 48,
                 Url = "/mediabrowser/dlna/icons/logo48.jpg"
             });
-            
+
             return list;
         }
 
@@ -175,8 +175,9 @@ namespace MediaBrowser.Dlna.Server
             {
                 ServiceType = "urn:schemas-upnp-org:service:ContentDirectory:1",
                 ServiceId = "urn:upnp-org:serviceId:ContentDirectory",
-                ScpdUrl = "/mediabrowser/dlna/contentdirectory.xml",
-                ControlUrl = "/mediabrowser/dlna/" + _serverUdn + "/control"
+                ScpdUrl = "/mediabrowser/dlna/contentdirectory/contentdirectory.xml",
+                ControlUrl = "/mediabrowser/dlna/contentdirectory/" + _serverUdn + "/control",
+                EventSubUrl = "/mediabrowser/dlna/contentdirectory/" + _serverUdn + "/events"
             });
 
             return list;

+ 12 - 0
MediaBrowser.Dlna/Server/Headers.cs

@@ -160,5 +160,17 @@ namespace MediaBrowser.Dlna.Server
         {
             return _dict.TryGetValue(Normalize(key), out value);
         }
+
+        public string GetValueOrDefault(string key, string defaultValue)
+        {
+            string val;
+
+            if (TryGetValue(key, out val))
+            {
+                return val;
+            }
+
+            return defaultValue;
+        }
     }
 }

+ 37 - 22
MediaBrowser.Dlna/Server/SsdpHandler.cs

@@ -107,7 +107,11 @@ namespace MediaBrowser.Dlna.Server
                             break;
                         }
                         var parts = line.Split(new[] { ':' }, 2);
-                        headers[parts[0]] = parts[1].Trim();
+
+                        if (parts.Length >= 2)
+                        {
+                            headers[parts[0]] = parts[1].Trim();
+                        }
                     }
 
                     if (_config.Configuration.DlnaOptions.EnableDebugLogging)
@@ -158,18 +162,21 @@ namespace MediaBrowser.Dlna.Server
 
         private void SendSearchResponse(IPEndPoint endpoint, UpnpDevice dev)
         {
-            var headers = new Headers(true);
-            headers.Add("CACHE-CONTROL", "max-age = 600");
-            headers.Add("DATE", DateTime.Now.ToString("R"));
-            headers.Add("EXT", "");
-            headers.Add("LOCATION", dev.Descriptor.ToString());
-            headers.Add("SERVER", _serverSignature);
-            headers.Add("ST", dev.Type);
-            headers.Add("USN", dev.USN);
+            var builder = new StringBuilder();
 
-            var msg = String.Format("HTTP/1.1 200 OK\r\n{0}\r\n", headers.HeaderBlock);
+            const string argFormat = "{0}: {1}\r\n";
 
-            SendDatagram(endpoint, dev.Address, msg, false);
+            builder.Append("HTTP/1.1 200 OK\r\n");
+            builder.AppendFormat(argFormat, "CACHE-CONTROL", "max-age = 600");
+            builder.AppendFormat(argFormat, "DATE", DateTime.Now.ToString("R"));
+            builder.AppendFormat(argFormat, "EXT", "");
+            builder.AppendFormat(argFormat, "LOCATION", dev.Descriptor);
+            builder.AppendFormat(argFormat, "SERVER", _serverSignature);
+            builder.AppendFormat(argFormat, "ST", dev.Type);
+            builder.AppendFormat(argFormat, "USN", dev.USN);
+            builder.Append("\r\n");
+
+            SendDatagram(endpoint, dev.Address, builder.ToString(), false);
 
             _logger.Info("{1} - Responded to a {0} request to {2}", dev.Type, endpoint, dev.Address.ToString());
         }
@@ -237,19 +244,22 @@ namespace MediaBrowser.Dlna.Server
         private void NotifyDevice(UpnpDevice dev, string type, bool sticky)
         {
             _logger.Debug("NotifyDevice");
-            var headers = new Headers(true);
-            headers.Add("HOST", "239.255.255.250:1900");
-            headers.Add("CACHE-CONTROL", "max-age = 600");
-            headers.Add("LOCATION", dev.Descriptor.ToString());
-            headers.Add("SERVER", _serverSignature);
-            headers.Add("NTS", "ssdp:" + type);
-            headers.Add("NT", dev.Type);
-            headers.Add("USN", dev.USN);
+            var builder = new StringBuilder();
+
+            const string argFormat = "{0}: {1}\r\n";
 
-            var msg = String.Format("NOTIFY * HTTP/1.1\r\n{0}\r\n", headers.HeaderBlock);
+            builder.Append("NOTIFY * HTTP/1.1\r\n{0}\r\n");
+            builder.AppendFormat(argFormat, "HOST", "239.255.255.250:1900");
+            builder.AppendFormat(argFormat, "CACHE-CONTROL", "max-age = 600");
+            builder.AppendFormat(argFormat, "LOCATION", dev.Descriptor);
+            builder.AppendFormat(argFormat, "SERVER", _serverSignature);
+            builder.AppendFormat(argFormat, "NTS", "ssdp:" + type);
+            builder.AppendFormat(argFormat, "NT", dev.Type);
+            builder.AppendFormat(argFormat, "USN", dev.USN);
+            builder.Append("\r\n");
 
             _logger.Debug("{0} said {1}", dev.USN, type);
-            SendDatagram(_ssdpEndp, dev.Address, msg, sticky);
+            SendDatagram(_ssdpEndp, dev.Address, builder.ToString(), sticky);
         }
 
         public void RegisterNotification(Guid uuid, Uri descriptor, IPAddress address)
@@ -343,7 +353,12 @@ namespace MediaBrowser.Dlna.Server
         private readonly object _notificationTimerSyncLock = new object();
         private void StartNotificationTimer()
         {
-            const int intervalMs = 60000;
+            if (!_config.Configuration.DlnaOptions.BlastAliveMessages)
+            {
+                return;
+            }
+
+            var intervalMs = _config.Configuration.DlnaOptions.BlastAliveMessageIntervalSeconds * 1000;
 
             lock (_notificationTimerSyncLock)
             {

+ 2 - 2
MediaBrowser.Dlna/Server/UpnpDevice.cs

@@ -19,13 +19,13 @@ namespace MediaBrowser.Dlna.Server
 
             Address = address;
 
-            if (Type.StartsWith("uuid:"))
+            if (Type.StartsWith("uuid:", StringComparison.OrdinalIgnoreCase))
             {
                 USN = Type;
             }
             else
             {
-                USN = String.Format("uuid:{0}::{1}", Uuid.ToString(), Type);
+                USN = String.Format("uuid:{0}::{1}", Uuid.ToString("N"), Type);
             }
         }
     }

+ 1 - 1
MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs

@@ -27,7 +27,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
         /// </summary>
         /// <param name="inputFiles">The input files.</param>
         /// <returns>System.String.</returns>
-        private static string GetConcatInputArgument(List<string> inputFiles)
+        private static string GetConcatInputArgument(IReadOnlyList<string> inputFiles)
         {
             // Get all streams
             // If there's more than one we'll need to use the concat command

+ 9 - 0
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\Filter.cs">
+      <Link>Dlna\Filter.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfile.cs">
       <Link>Dlna\MediaFormatProfile.cs</Link>
     </Compile>
@@ -131,6 +134,12 @@
     <Compile Include="..\MediaBrowser.Model\Dlna\ResponseProfile.cs">
       <Link>Dlna\ResponseProfile.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\SearchCriteria.cs">
+      <Link>Dlna\SearchCriteria.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\SortCriteria.cs">
+      <Link>Dlna\SortCriteria.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dlna\StreamBuilder.cs">
       <Link>Dlna\StreamBuilder.cs</Link>
     </Compile>

+ 9 - 0
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\Filter.cs">
+      <Link>Dlna\Filter.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfile.cs">
       <Link>Dlna\MediaFormatProfile.cs</Link>
     </Compile>
@@ -118,6 +121,12 @@
     <Compile Include="..\MediaBrowser.Model\Dlna\ResponseProfile.cs">
       <Link>Dlna\ResponseProfile.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\SearchCriteria.cs">
+      <Link>Dlna\SearchCriteria.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\SortCriteria.cs">
+      <Link>Dlna\SortCriteria.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dlna\StreamBuilder.cs">
       <Link>Dlna\StreamBuilder.cs</Link>
     </Compile>

+ 5 - 0
MediaBrowser.Model/Configuration/DlnaOptions.cs

@@ -6,13 +6,18 @@ namespace MediaBrowser.Model.Configuration
         public bool EnablePlayTo { get; set; }
         public bool EnableServer { get; set; }
         public bool EnableDebugLogging { get; set; }
+        public bool BlastAliveMessages { get; set; }
         public int ClientDiscoveryIntervalSeconds { get; set; }
+        public int BlastAliveMessageIntervalSeconds { get; set; }
+        public string DefaultUserId { get; set; }
 
         public DlnaOptions()
         {
             EnablePlayTo = true;
             EnableServer = true;
+            BlastAliveMessages = true;
             ClientDiscoveryIntervalSeconds = 60;
+            BlastAliveMessageIntervalSeconds = 60;
         }
     }
 }

+ 2 - 0
MediaBrowser.Model/Dlna/DeviceProfile.cs

@@ -40,6 +40,8 @@ namespace MediaBrowser.Model.Dlna
 
         public string SupportedMediaTypes { get; set; }
 
+        public string UserId { get; set; }
+        
         /// <summary>
         /// Controls the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.
         /// </summary>

+ 26 - 0
MediaBrowser.Model/Dlna/Filter.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MediaBrowser.Model.Dlna
+{
+    public class Filter
+    {
+        private readonly List<string> _fields;
+        private readonly bool _all;
+
+        public Filter(string filter)
+        {
+            _all = string.Equals(filter, "*", StringComparison.OrdinalIgnoreCase);
+
+            _fields = (filter ?? string.Empty)
+                .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
+                .ToList();
+        }
+
+        public bool Contains(string field)
+        {
+            return _all || _fields.Contains(field, StringComparer.OrdinalIgnoreCase);
+        }
+    }
+}

+ 49 - 0
MediaBrowser.Model/Dlna/SearchCriteria.cs

@@ -0,0 +1,49 @@
+using System;
+
+namespace MediaBrowser.Model.Dlna
+{
+    public class SearchCriteria
+    {
+        public SearchType SearchType { get; set; }
+
+        public SearchCriteria(string search)
+        {
+            if (string.IsNullOrEmpty(search))
+            {
+                throw new ArgumentNullException("search");
+            }
+
+            SearchType = SearchType.Unknown;
+
+            if (search.IndexOf("upnp:class", StringComparison.OrdinalIgnoreCase) != -1 &&
+                search.IndexOf("derivedfrom", StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                if (search.IndexOf("object.item.audioItem", StringComparison.OrdinalIgnoreCase) != -1)
+                {
+                    SearchType = SearchType.Audio;
+                }
+                else if (search.IndexOf("object.item.imageItem", StringComparison.OrdinalIgnoreCase) != -1)
+                {
+                    SearchType = SearchType.Image;
+                }
+                else if (search.IndexOf("object.item.videoItem", StringComparison.OrdinalIgnoreCase) != -1)
+                {
+                    SearchType = SearchType.Video;
+                }
+                else if (search.IndexOf("object.container.playlistContainer", StringComparison.OrdinalIgnoreCase) != -1)
+                {
+                    SearchType = SearchType.Playlist;
+                }
+            }
+        }
+    }
+
+    public enum SearchType
+    {
+        Unknown = 0,
+        Audio = 1,
+        Image = 2,
+        Video = 3,
+        Playlist = 4
+    }
+}

+ 11 - 0
MediaBrowser.Model/Dlna/SortCriteria.cs

@@ -0,0 +1,11 @@
+
+namespace MediaBrowser.Model.Dlna
+{
+    public class SortCriteria
+    {
+        public SortCriteria(string value)
+        {
+            
+        }
+    }
+}

+ 3 - 0
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -73,9 +73,12 @@
     <Compile Include="Dlna\DeviceProfileInfo.cs" />
     <Compile Include="Dlna\DirectPlayProfile.cs" />
     <Compile Include="Dlna\DlnaMaps.cs" />
+    <Compile Include="Dlna\Filter.cs" />
     <Compile Include="Dlna\MediaFormatProfile.cs" />
     <Compile Include="Dlna\MediaFormatProfileResolver.cs" />
     <Compile Include="Dlna\ResponseProfile.cs" />
+    <Compile Include="Dlna\SearchCriteria.cs" />
+    <Compile Include="Dlna\SortCriteria.cs" />
     <Compile Include="Dlna\StreamBuilder.cs" />
     <Compile Include="Dlna\StreamInfo.cs" />
     <Compile Include="Dlna\TranscodingProfile.cs" />

+ 10 - 1
MediaBrowser.Providers/Movies/MovieXmlProvider.cs

@@ -44,7 +44,16 @@ namespace MediaBrowser.Providers.Movies
 
             var file = new FileInfo(specificFile);
 
-            return info.IsInMixedFolder || file.Exists ? file : new FileInfo(Path.Combine(directoryPath, "movie.xml"));
+            // In a mixed folder, only {moviename}.xml is supported
+            if (info.IsInMixedFolder)
+            {
+                return file;
+            }
+
+            // If in it's own folder, prefer movie.xml, but allow the specific file as well
+            var movieFile = new FileInfo(Path.Combine(directoryPath, "movie.xml"));
+
+            return movieFile.Exists ? movieFile : file;
         }
     }
 }

File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/ar.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/ca.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/cs.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/de.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/el.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/en_GB.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/en_US.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/es.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/es_MX.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/fr.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/he.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/it.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/kk.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/ms.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/nb.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/nl.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/ru.json


+ 12 - 2
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -414,7 +414,7 @@
 	"LabelEnableDlnaDebugLogging": "Enable DLNA debug logging",
 	"LabelEnableDlnaDebugLoggingHelp": "This will create large log files and should only be used as needed for troubleshooting purposes.",
 	"LabelEnableDlnaClientDiscoveryInterval": "Client discovery interval (seconds)",
-	"LabelEnableDlnaClientDiscoveryIntervalHelp": "Determines the duration in seconds of the interval between SSDP searches performed by Media Browser.",
+	"LabelEnableDlnaClientDiscoveryIntervalHelp": "Determines the duration in seconds between SSDP searches performed by Media Browser.",
 	"HeaderCustomDlnaProfiles": "Custom Profiles",
 	"HeaderSystemDlnaProfiles": "System Profiles",
 	"CustomDlnaProfilesHelp": "Create a custom profile to target a new device or override a system profile.",
@@ -532,5 +532,15 @@
 	"LabelSupporterKey": "Supporter Key (paste from email)",
 	"LabelSupporterKeyHelp": "Enter your supporter key to start enjoying additional benefits the community has developed for Media Browser.",
 	"MessageInvalidKey": "MB3 Key Missing or Invalid",
-	"ErrorMessageInvalidKey": "In order for any premium content to be registered, you must also be an MB3 Supporter. Please donate and support the continued development of the core product. Thank you."
+	"ErrorMessageInvalidKey": "In order for any premium content to be registered, you must also be an MB3 Supporter. Please donate and support the continued development of the core product. Thank you.",
+	"HeaderDisplaySettings": "Display Settings",
+	"TabPlayTo": "Play To",
+	"LabelEnableDlnaServer": "Enable Dlna server",
+	"LabelEnableDlnaServerHelp": "Allows UPnP devices on your network to browse and play Media Browser content.",
+	"LabelEnableBlastAliveMessages": "Blast alive messages",
+	"LabelEnableBlastAliveMessagesHelp": "Enable this if the server is not detected reliably by other UPnP devices on your network.",
+	"LabelBlastMessageInterval": "Alive message interval (seconds)",
+	"LabelBlastMessageIntervalHelp": "Determines the duration in seconds between server alive messages.",
+	"LabelDefaultUser": "Default user:",
+	"LabelDefaultUserHelp": "Determines which user library should be displayed on connected devices. This can be overridden using a device profile."
 }

File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/sv.json


File diff suppressed because it is too large
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json


+ 1 - 1
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -506,7 +506,7 @@ 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);
+            var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("DLNA"), JsonSerializer, UserManager, LibraryManager, DtoService, ImageProcessor, UserDataManager, ServerConfigurationManager);
             RegisterSingleInstance<IDlnaManager>(dlnaManager);
 
             var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor);

+ 1 - 0
MediaBrowser.WebDashboard/Api/DashboardService.cs

@@ -543,6 +543,7 @@ namespace MediaBrowser.WebDashboard.Api
                                 "dlnaprofile.js",
                                 "dlnaprofiles.js",
                                 "dlnasettings.js",
+                                "dlnaserversettings.js",
                                 "editcollectionitems.js",
                                 "edititemmetadata.js",
                                 "edititempeople.js",

+ 6 - 0
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -241,6 +241,9 @@
     <Content Include="dashboard-ui\dlnaprofiles.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\dlnaserversettings.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\dlnasettings.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -541,6 +544,9 @@
     <Content Include="dashboard-ui\scripts\dlnaprofiles.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\scripts\dlnaserversettings.js">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\scripts\dlnasettings.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>

Some files were not shown because too many files changed in this diff