Browse Source

support channels with dlna

Luke Pulverenti 11 years ago
parent
commit
51e964dae3
93 changed files with 936 additions and 315 deletions
  1. 1 1
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  2. 1 1
      MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
  3. 1 1
      MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
  4. 1 1
      MediaBrowser.Api/Playback/Progressive/VideoService.cs
  5. 1 1
      MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs
  6. 0 6
      MediaBrowser.Common/Net/IServerManager.cs
  7. 16 0
      MediaBrowser.Controller/Channels/IChannelManager.cs
  8. 1 1
      MediaBrowser.Controller/Entities/BasePluginFolder.cs
  9. 31 0
      MediaBrowser.Controller/Entities/UserView.cs
  10. 81 8
      MediaBrowser.Controller/Library/TVUtils.cs
  11. 7 16
      MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs
  12. 9 2
      MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs
  13. 164 56
      MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
  14. 9 1
      MediaBrowser.Dlna/Didl/DidlBuilder.cs
  15. BIN
      MediaBrowser.Dlna/Images/logo120.jpg
  16. BIN
      MediaBrowser.Dlna/Images/logo120.png
  17. BIN
      MediaBrowser.Dlna/Images/logo240.jpg
  18. BIN
      MediaBrowser.Dlna/Images/logo240.png
  19. BIN
      MediaBrowser.Dlna/Images/logo48.jpg
  20. BIN
      MediaBrowser.Dlna/Images/logo48.png
  21. 4 0
      MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
  22. 2 2
      MediaBrowser.Dlna/PlayTo/Device.cs
  23. 37 30
      MediaBrowser.Dlna/PlayTo/PlayToManager.cs
  24. 2 0
      MediaBrowser.Dlna/Profiles/DefaultProfile.cs
  25. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Android.xml
  26. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Default.xml
  27. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml
  28. 1 1
      MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml
  29. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml
  30. 1 1
      MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml
  31. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml
  32. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
  33. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
  34. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml
  35. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml
  36. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml
  37. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml
  38. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml
  39. 1 1
      MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml
  40. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml
  41. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml
  42. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml
  43. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml
  44. 1 1
      MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml
  45. 18 0
      MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs
  46. 7 2
      MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
  47. 7 1
      MediaBrowser.Model/Dlna/SortCriteria.cs
  48. 1 1
      MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs
  49. 58 9
      MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
  50. 0 5
      MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
  51. 5 2
      MediaBrowser.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
  52. 27 16
      MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
  53. 5 3
      MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
  54. 5 13
      MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
  55. 40 2
      MediaBrowser.Server.Implementations/Library/UserViewManager.cs
  56. 1 1
      MediaBrowser.Server.Implementations/LiveTv/CleanDatabaseScheduledTask.cs
  57. 0 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
  58. 4 4
      MediaBrowser.Server.Implementations/Localization/JavaScript/kk.json
  59. 1 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json
  60. 3 3
      MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json
  61. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/ar.json
  62. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/ca.json
  63. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/cs.json
  64. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/da.json
  65. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/de.json
  66. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/el.json
  67. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/en_GB.json
  68. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/en_US.json
  69. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/es.json
  70. 13 1
      MediaBrowser.Server.Implementations/Localization/Server/es_MX.json
  71. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/fr.json
  72. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/he.json
  73. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/it.json
  74. 3 3
      MediaBrowser.Server.Implementations/Localization/Server/kk.json
  75. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/ko.json
  76. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/ms.json
  77. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/nb.json
  78. 28 18
      MediaBrowser.Server.Implementations/Localization/Server/nl.json
  79. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/pl.json
  80. 16 6
      MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json
  81. 27 17
      MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json
  82. 19 9
      MediaBrowser.Server.Implementations/Localization/Server/ru.json
  83. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/sv.json
  84. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/vi.json
  85. 11 1
      MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json
  86. 0 9
      MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs
  87. 25 0
      MediaBrowser.Server.Implementations/Session/WebSocketController.cs
  88. 11 3
      MediaBrowser.Server.Implementations/Udp/UdpServer.cs
  89. 1 1
      MediaBrowser.Server.Mono/Native/ServerAuthorization.cs
  90. 7 7
      MediaBrowser.ServerApplication/ApplicationHost.cs
  91. 4 4
      MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs
  92. 1 7
      MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs
  93. 2 0
      MediaBrowser.Tests/Resolvers/TvUtilTests.cs

+ 1 - 1
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -626,7 +626,7 @@ namespace MediaBrowser.Api.Playback
         /// <param name="state">The state.</param>
         /// <param name="outputVideoCodec">The output video codec.</param>
         /// <returns>System.String.</returns>
-        protected string GetInternalGraphicalSubtitleParam(StreamState state, string outputVideoCodec)
+        protected string GetGraphicalSubtitleParam(StreamState state, string outputVideoCodec)
         {
             var outputSizeParam = string.Empty;
 

+ 1 - 1
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -522,7 +522,7 @@ namespace MediaBrowser.Api.Playback.Hls
             // This is for internal graphical subs
             if (hasGraphicalSubs)
             {
-                args += GetInternalGraphicalSubtitleParam(state, codec);
+                args += GetGraphicalSubtitleParam(state, codec);
             }
 
             return args;

+ 1 - 1
MediaBrowser.Api/Playback/Hls/VideoHlsService.cs

@@ -186,7 +186,7 @@ namespace MediaBrowser.Api.Playback.Hls
             // This is for internal graphical subs
             if (hasGraphicalSubs)
             {
-                args += GetInternalGraphicalSubtitleParam(state, codec);
+                args += GetGraphicalSubtitleParam(state, codec);
             }
 
             return args;

+ 1 - 1
MediaBrowser.Api/Playback/Progressive/VideoService.cs

@@ -167,7 +167,7 @@ namespace MediaBrowser.Api.Playback.Progressive
             // This is for internal graphical subs
             if (hasGraphicalSubs)
             {
-                args += GetInternalGraphicalSubtitleParam(state, codec);
+                args += GetGraphicalSubtitleParam(state, codec);
             }
 
             return args;

+ 1 - 1
MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs

@@ -94,7 +94,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
 
         public bool IsHidden
         {
-            get { return false; }
+            get { return true; }
         }
 
         public bool IsEnabled

+ 0 - 6
MediaBrowser.Common/Net/IServerManager.cs

@@ -10,12 +10,6 @@ namespace MediaBrowser.Common.Net
     /// </summary>
     public interface IServerManager : IDisposable
     {
-        /// <summary>
-        /// Gets the web socket port number.
-        /// </summary>
-        /// <value>The web socket port number.</value>
-        int WebSocketPortNumber { get; }
-
         /// <summary>
         /// Starts this instance.
         /// </summary>

+ 16 - 0
MediaBrowser.Controller/Channels/IChannelManager.cs

@@ -43,6 +43,14 @@ namespace MediaBrowser.Controller.Channels
         /// <returns>Channel.</returns>
         Channel GetChannel(string id);
 
+        /// <summary>
+        /// Gets the channels internal.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task&lt;QueryResult&lt;Channel&gt;&gt;.</returns>
+        Task<QueryResult<Channel>> GetChannelsInternal(ChannelQuery query, CancellationToken cancellationToken);
+
         /// <summary>
         /// Gets the channels.
         /// </summary>
@@ -75,6 +83,14 @@ namespace MediaBrowser.Controller.Channels
         /// <returns>Task{QueryResult{BaseItemDto}}.</returns>
         Task<QueryResult<BaseItemDto>> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken);
 
+        /// <summary>
+        /// Gets the channel items internal.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task&lt;QueryResult&lt;BaseItem&gt;&gt;.</returns>
+        Task<QueryResult<BaseItem>> GetChannelItemsInternal(ChannelItemQuery query, CancellationToken cancellationToken);
+
         /// <summary>
         /// Gets the cached channel item media sources.
         /// </summary>

+ 1 - 1
MediaBrowser.Controller/Entities/BasePluginFolder.cs

@@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Entities
 
         public virtual string CollectionType
         {
-            get { return Model.Entities.CollectionType.BoxSets; }
+            get { return null; }
         }
     }
 }

+ 31 - 0
MediaBrowser.Controller/Entities/UserView.cs

@@ -1,6 +1,7 @@
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Model.Entities;
+using MoreLinq;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -29,6 +30,10 @@ namespace MediaBrowser.Controller.Entities
                 case CollectionType.Trailers:
                     return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren))
                         .OfType<Trailer>();
+                case CollectionType.Movies:
+                    return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren))
+                        .Where(i => i is Movie || i is BoxSet)
+                        .DistinctBy(i => i.Id);
                 default:
                     return mediaFolders.SelectMany(i => i.GetChildren(user, includeLinkedChildren));
             }
@@ -70,4 +75,30 @@ namespace MediaBrowser.Controller.Entities
             return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty);
         }
     }
+
+    public class SpecialFolder : Folder
+    {
+        public SpecialFolderType SpecialFolderType { get; set; }
+        public string ItemTypeName { get; set; }
+        public string ParentId { get; set; }
+
+        public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
+        {
+            var parent = (Folder)LibraryManager.GetItemById(new Guid(ParentId));
+
+            if (SpecialFolderType == SpecialFolderType.ItemsByType)
+            {
+                var items = parent.GetRecursiveChildren(user, includeLinkedChildren);
+
+                return items.Where(i => string.Equals(i.GetType().Name, ItemTypeName, StringComparison.OrdinalIgnoreCase));
+            }
+
+            return new List<BaseItem>();
+        }
+    }
+
+    public enum SpecialFolderType
+    {
+        ItemsByType = 1
+    }
 }

+ 81 - 8
MediaBrowser.Controller/Library/TVUtils.cs

@@ -8,6 +8,7 @@ using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Text.RegularExpressions;
+using MediaBrowser.Model.Logging;
 
 namespace MediaBrowser.Controller.Library
 {
@@ -136,6 +137,16 @@ namespace MediaBrowser.Controller.Library
                 return val;
             }
 
+            if (filename.StartsWith("s", StringComparison.OrdinalIgnoreCase))
+            {
+                var testFilename = filename.Substring(1);
+
+                if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out val))
+                {
+                    return val;
+                }
+            }
+
             // Look for one of the season folder names
             foreach (var name in SeasonFolderNames)
             {
@@ -182,7 +193,7 @@ namespace MediaBrowser.Controller.Library
                 return null;
             }
 
-            return int.Parse(path.Substring(numericStart, length));
+            return int.Parse(path.Substring(numericStart, length), CultureInfo.InvariantCulture);
         }
 
         /// <summary>
@@ -194,21 +205,59 @@ namespace MediaBrowser.Controller.Library
         /// <returns><c>true</c> if [is season folder] [the specified path]; otherwise, <c>false</c>.</returns>
         private static bool IsSeasonFolder(string path, IDirectoryService directoryService, IFileSystem fileSystem)
         {
+            var seasonNumber = GetSeasonNumberFromPath(path);
+            var hasSeasonNumber = seasonNumber != null;
+
+            if (!hasSeasonNumber)
+            {
+                return false;
+            }
+
             // It's a season folder if it's named as such and does not contain any audio files, apart from theme.mp3
-            return GetSeasonNumberFromPath(path) != null && 
-                !directoryService.GetFiles(path)
-                .Any(i => EntityResolutionHelper.IsAudioFile(i.FullName) && !string.Equals(fileSystem.GetFileNameWithoutExtension(i), BaseItem.ThemeSongFilename));
+            foreach (var fileSystemInfo in directoryService.GetFileSystemEntries(path))
+            {
+                var attributes = fileSystemInfo.Attributes;
+
+                if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
+                {
+                    continue;
+                }
+
+                if ((attributes & FileAttributes.System) == FileAttributes.System)
+                {
+                    continue;
+                }
+
+                if ((attributes & FileAttributes.Directory) == FileAttributes.Directory)
+                {
+                    //if (IsBadFolder(fileSystemInfo.Name))
+                    //{
+                    //    return false;
+                    //}
+                }
+                else
+                {
+                    if (EntityResolutionHelper.IsAudioFile(fileSystemInfo.FullName) &&
+                        !string.Equals(fileSystem.GetFileNameWithoutExtension(fileSystemInfo), BaseItem.ThemeSongFilename))
+                    {
+                        return false;
+                    }
+                }
+            }
+
+            return true;
         }
 
         /// <summary>
         /// Determines whether [is series folder] [the specified path].
         /// </summary>
         /// <param name="path">The path.</param>
-        /// <param name="considerSeasonlessSeries">if set to <c>true</c> [consider seasonless series].</param>
+        /// <param name="considerSeasonlessEntries">if set to <c>true</c> [consider seasonless entries].</param>
         /// <param name="fileSystemChildren">The file system children.</param>
         /// <param name="directoryService">The directory service.</param>
+        /// <param name="fileSystem">The file system.</param>
         /// <returns><c>true</c> if [is series folder] [the specified path]; otherwise, <c>false</c>.</returns>
-        public static bool IsSeriesFolder(string path, bool considerSeasonlessSeries, IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService, IFileSystem fileSystem)
+        public static bool IsSeriesFolder(string path, bool considerSeasonlessEntries, IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService, IFileSystem fileSystem, ILogger logger)
         {
             // A folder with more than 3 non-season folders in will not becounted as a series
             var nonSeriesFolders = 0;
@@ -231,15 +280,20 @@ namespace MediaBrowser.Controller.Library
                 {
                     if (IsSeasonFolder(child.FullName, directoryService, fileSystem))
                     {
+                        logger.Debug("{0} is a series because of season folder {1}.", path, child.FullName);
                         return true;
                     }
-                    if (!EntityResolutionHelper.IgnoreFolders.Contains(child.Name, StringComparer.OrdinalIgnoreCase))
+
+                    if (IsBadFolder(child.Name))
                     {
+                        logger.Debug("Invalid folder under series: {0}", child.FullName);
+
                         nonSeriesFolders++;
                     }
 
                     if (nonSeriesFolders >= 3)
                     {
+                        logger.Debug("{0} not a series due to 3 or more invalid folders.", path);
                         return false;
                     }
                 }
@@ -249,7 +303,7 @@ namespace MediaBrowser.Controller.Library
 
                     if (EntityResolutionHelper.IsVideoFile(fullName) || EntityResolutionHelper.IsVideoPlaceHolder(fullName))
                     {
-                        if (GetEpisodeNumberFromFile(fullName, considerSeasonlessSeries).HasValue)
+                        if (GetEpisodeNumberFromFile(fullName, considerSeasonlessEntries).HasValue)
                         {
                             return true;
                         }
@@ -257,9 +311,28 @@ namespace MediaBrowser.Controller.Library
                 }
             }
 
+            logger.Debug("{0} is not a series folder.", path);
             return false;
         }
 
+        private static bool IsBadFolder(string name)
+        {
+            if (string.Equals(name, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
+            {
+                return false;
+            }
+            if (string.Equals(name, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
+            {
+                return false;
+            }
+            if (string.Equals(name, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase))
+            {
+                return false;
+            }
+
+            return !EntityResolutionHelper.IgnoreFolders.Contains(name, StringComparer.OrdinalIgnoreCase);
+        }
+
         /// <summary>
         /// Episodes the number from file.
         /// </summary>

+ 7 - 16
MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs

@@ -45,7 +45,7 @@ namespace MediaBrowser.Dlna.Channels
             _localServersLookup = localServersLookup;
 
             _deviceDiscovery = deviceDiscovery;
-            deviceDiscovery.DeviceDiscovered += deviceDiscovery_DeviceDiscovered;
+            //deviceDiscovery.DeviceDiscovered += deviceDiscovery_DeviceDiscovered;
             deviceDiscovery.DeviceLeft += deviceDiscovery_DeviceLeft;
         }
 
@@ -196,25 +196,16 @@ namespace MediaBrowser.Dlna.Channels
 
     public class ServerChannel : IChannel, IFactoryChannel
     {
-        private readonly List<Device> _servers = new List<Device>();
         private readonly IHttpClient _httpClient;
         private readonly ILogger _logger;
-        private readonly string _controlUrl;
+        public  string ControlUrl { get; set; }
+        public List<Device> Servers { get; set; }
 
-        /// <summary>
-        /// Prevents core from throwing an exception
-        /// </summary>
-        public ServerChannel()
+        public ServerChannel(IHttpClient httpClient, ILogger logger)
         {
-            
-        }
-
-        public ServerChannel(List<Device> servers, IHttpClient httpClient, ILogger logger, string controlUrl)
-        {
-            _servers = servers;
             _httpClient = httpClient;
             _logger = logger;
-            _controlUrl = controlUrl;
+            Servers = new List<Device>();
         }
 
         public string Name
@@ -272,7 +263,7 @@ namespace MediaBrowser.Dlna.Channels
 
             if (string.IsNullOrWhiteSpace(query.FolderId))
             {
-                items = _servers.Select(i => new ChannelItemInfo
+                items = Servers.Select(i => new ChannelItemInfo
                 {
                     FolderType = ChannelFolderType.Container,
                     Id = GetServerId(i),
@@ -291,7 +282,7 @@ namespace MediaBrowser.Dlna.Channels
                     Limit = query.Limit,
                     StartIndex = query.StartIndex,
                     ParentId = folderId,
-                    ContentDirectoryUrl = _controlUrl
+                    ContentDirectoryUrl = ControlUrl
 
                 }, cancellationToken).ConfigureAwait(false);
 

+ 9 - 2
MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Drawing;
@@ -21,6 +22,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
         private readonly IDlnaManager _dlna;
         private readonly IServerConfigurationManager _config;
         private readonly IUserManager _userManager;
+        private readonly IUserViewManager _userViewManager;
+        private readonly IChannelManager _channelManager;
 
         public ContentDirectory(IDlnaManager dlna,
             IUserDataManager userDataManager,
@@ -29,7 +32,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             IServerConfigurationManager config,
             IUserManager userManager,
             ILogger logger,
-            IHttpClient httpClient)
+            IHttpClient httpClient, IUserViewManager userViewManager, IChannelManager channelManager)
             : base(logger, httpClient)
         {
             _dlna = dlna;
@@ -38,6 +41,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
             _libraryManager = libraryManager;
             _config = config;
             _userManager = userManager;
+            _userViewManager = userViewManager;
+            _channelManager = channelManager;
         }
 
         private int SystemUpdateId
@@ -73,7 +78,9 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 _userDataManager,
                 user,
                 SystemUpdateId,
-                _config)
+                _config,
+                _userViewManager,
+                _channelManager)
                 .ProcessControlRequest(request);
         }
 

+ 164 - 56
MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
@@ -9,8 +10,10 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Dlna.Didl;
 using MediaBrowser.Dlna.Server;
 using MediaBrowser.Dlna.Service;
+using MediaBrowser.Model.Channels;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Library;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Querying;
 using System;
@@ -19,6 +22,7 @@ using System.Globalization;
 using System.Linq;
 using System.Text;
 using System.Threading;
+using System.Threading.Tasks;
 using System.Xml;
 
 namespace MediaBrowser.Dlna.ContentDirectory
@@ -40,14 +44,18 @@ namespace MediaBrowser.Dlna.ContentDirectory
         private readonly DidlBuilder _didlBuilder;
 
         private readonly DeviceProfile _profile;
+        private readonly IUserViewManager _userViewManager;
+        private readonly IChannelManager _channelManager;
 
-        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config)
+        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, IUserViewManager userViewManager, IChannelManager channelManager)
             : base(config, logger)
         {
             _libraryManager = libraryManager;
             _userDataManager = userDataManager;
             _user = user;
             _systemUpdateId = systemUpdateId;
+            _userViewManager = userViewManager;
+            _channelManager = channelManager;
             _profile = profile;
 
             _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress);
@@ -69,7 +77,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 return HandleGetSystemUpdateID();
 
             if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase))
-                return HandleBrowse(methodParams, user, deviceId);
+                return HandleBrowse(methodParams, user, deviceId).Result;
 
             if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
                 return HandleXGetFeatureList();
@@ -78,7 +86,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 return HandleXSetBookmark(methodParams, user);
 
             if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase))
-                return HandleSearch(methodParams, user, deviceId);
+                return HandleSearch(methodParams, user, deviceId).Result;
 
             throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
         }
@@ -141,7 +149,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             return builder.ToString();
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleBrowse(Headers sparams, User user, string deviceId)
+        private async Task<IEnumerable<KeyValuePair<string, string>>> HandleBrowse(Headers sparams, User user, string deviceId)
         {
             var id = sparams["ObjectID"];
             var flag = sparams["BrowseFlag"];
@@ -149,16 +157,20 @@ namespace MediaBrowser.Dlna.ContentDirectory
             var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", ""));
 
             var provided = 0;
-            var requested = 0;
-            var start = 0;
 
-            if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requested) && requested <= 0)
+            int? requested = 0;
+            int? start = 0;
+
+            int requestedVal;
+            if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requestedVal) && requestedVal > 0)
             {
-                requested = 0;
+                requested = requestedVal;
             }
-            if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out start) && start <= 0)
+
+            int startVal;
+            if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out startVal) && startVal > 0)
             {
-                start = 0;
+                start = startVal;
             }
 
             //var root = GetItem(id) as IMediaFolder;
@@ -173,34 +185,26 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
             var folder = (Folder)GetItemFromObjectId(id, user);
 
-            var children = GetChildrenSorted(folder, user, sortCriteria).ToList();
+            var childrenResult = (await GetChildrenSorted(folder, user, sortCriteria, start, requested).ConfigureAwait(false));
 
-            var totalCount = children.Count;
+            var totalCount = childrenResult.TotalRecordCount;
 
             if (string.Equals(flag, "BrowseMetadata"))
             {
-                result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, folder, children.Count, filter));
+                result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, folder, totalCount, filter));
                 provided++;
             }
             else
             {
-                if (start > 0)
-                {
-                    children = children.Skip(start).ToList();
-                }
-                if (requested > 0)
-                {
-                    children = children.Take(requested).ToList();
-                }
-
-                provided = children.Count;
+                provided = childrenResult.Items.Length;
 
-                foreach (var i in children)
+                foreach (var i in childrenResult.Items)
                 {
                     if (i.IsFolder)
                     {
                         var f = (Folder)i;
-                        var childCount = GetChildrenSorted(f, user, sortCriteria).Count();
+                        var childCount = (await GetChildrenSorted(f, user, sortCriteria, null, 0).ConfigureAwait(false))
+                            .TotalRecordCount;
 
                         result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter));
                     }
@@ -222,7 +226,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             };
         }
 
-        private IEnumerable<KeyValuePair<string, string>> HandleSearch(Headers sparams, User user, string deviceId)
+        private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(Headers sparams, User user, string deviceId)
         {
             var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", ""));
             var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", ""));
@@ -230,16 +234,19 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
             // sort example: dc:title, dc:date
 
-            var requested = 0;
-            var start = 0;
+            int? requested = 0;
+            int? start = 0;
 
-            if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requested) && requested <= 0)
+            int requestedVal;
+            if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requestedVal) && requestedVal > 0)
             {
-                requested = 0;
+                requested = requestedVal;
             }
-            if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out start) && start <= 0)
+
+            int startVal;
+            if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out startVal) && startVal > 0)
             {
-                start = 0;
+                start = startVal;
             }
 
             //var root = GetItem(id) as IMediaFolder;
@@ -259,27 +266,19 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
             var folder = (Folder)GetItemFromObjectId(sparams["ContainerID"], user);
 
-            var children = GetChildrenSorted(folder, user, searchCriteria, sortCriteria).ToList();
+            var childrenResult = (await GetChildrenSorted(folder, user, searchCriteria, sortCriteria, start, requested).ConfigureAwait(false));
 
-            var totalCount = children.Count;
+            var totalCount = childrenResult.TotalRecordCount;
 
-            if (start > 0)
-            {
-                children = children.Skip(start).ToList();
-            }
-            if (requested > 0)
-            {
-                children = children.Take(requested).ToList();
-            }
-
-            var provided = children.Count;
+            var provided = childrenResult.Items.Length;
 
-            foreach (var i in children)
+            foreach (var i in childrenResult.Items)
             {
                 if (i.IsFolder)
                 {
                     var f = (Folder)i;
-                    var childCount = GetChildrenSorted(f, user, searchCriteria, sortCriteria).Count();
+                    var childCount = (await GetChildrenSorted(f, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false))
+                        .TotalRecordCount;
 
                     result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter));
                 }
@@ -300,15 +299,16 @@ namespace MediaBrowser.Dlna.ContentDirectory
             };
         }
 
-        private IEnumerable<BaseItem> GetChildrenSorted(Folder folder, User user, SearchCriteria search, SortCriteria sort)
+        private async Task<QueryResult<BaseItem>> GetChildrenSorted(Folder folder, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
         {
             if (search.SearchType == SearchType.Unknown)
             {
-                return GetChildrenSorted(folder, user, sort);
+                return await GetChildrenSorted(folder, user, sort, startIndex, limit).ConfigureAwait(false);
             }
 
-            var items = folder.GetRecursiveChildren(user);
-            items = FilterUnsupportedContent(items);
+            var result = await GetChildrenSorted(folder, user, sort, null, null).ConfigureAwait(false);
+
+            var items = FilterUnsupportedContent(result.Items);
 
             if (search.SearchType == SearchType.Audio)
             {
@@ -324,12 +324,123 @@ namespace MediaBrowser.Dlna.ContentDirectory
             }
             else if (search.SearchType == SearchType.Playlist)
             {
+
             }
 
-            return SortItems(items, user, sort);
+            items = SortItems(items, user, sort);
+
+            return ToResult(items, startIndex, limit);
+        }
+
+        private async Task<QueryResult<BaseItem>> GetChildrenSorted(Folder folder, User user, SortCriteria sort, int? startIndex, int? limit)
+        {
+            if (folder is UserRootFolder)
+            {
+                var result = await _userViewManager.GetUserViews(new UserViewQuery
+                {
+                    UserId = user.Id.ToString("N")
+
+                }, CancellationToken.None).ConfigureAwait(false);
+
+                return ToResult(result, startIndex, limit);
+            }
+
+            var view = folder as UserView;
+
+            if (view != null)
+            {
+                var result = await GetUserViewChildren(view, user, sort).ConfigureAwait(false);
+
+                return ToResult(result, startIndex, limit);
+            }
+
+            var channel = folder as Channel;
+
+            if (channel != null)
+            {
+                return await _channelManager.GetChannelItemsInternal(new ChannelItemQuery
+                {
+                    ChannelId = channel.Id.ToString("N"),
+                    Limit = limit,
+                    StartIndex = startIndex,
+                    UserId = user.Id.ToString("N")
+
+                }, CancellationToken.None);
+            }
+
+            var channelFolderItem = folder as ChannelFolderItem;
+
+            if (channelFolderItem != null)
+            {
+                return await _channelManager.GetChannelItemsInternal(new ChannelItemQuery
+                {
+                    ChannelId = channelFolderItem.ChannelId,
+                    FolderId = channelFolderItem.Id.ToString("N"),
+                    Limit = limit,
+                    StartIndex = startIndex,
+                    UserId = user.Id.ToString("N")
+
+                }, CancellationToken.None);
+            }
+
+            return ToResult(GetPlainFolderChildrenSorted(folder, user, sort), startIndex, limit);
+        }
+
+        private QueryResult<BaseItem> ToResult(IEnumerable<BaseItem> items, int? startIndex, int? limit)
+        {
+            var list = items.ToArray();
+            var totalCount = list.Length;
+
+            if (startIndex.HasValue)
+            {
+                list = list.Skip(startIndex.Value).ToArray();
+            }
+
+            if (limit.HasValue)
+            {
+                list = list.Take(limit.Value).ToArray();
+            }
+
+            return new QueryResult<BaseItem>
+            {
+                Items = list,
+                TotalRecordCount = totalCount
+            };
+        }
+
+        private async Task<IEnumerable<BaseItem>> GetUserViewChildren(UserView folder, User user, SortCriteria sort)
+        {
+            if (string.Equals(folder.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
+            {
+                return new List<BaseItem>();
+            }
+            if (string.Equals(folder.ViewType, CollectionType.Channels, StringComparison.OrdinalIgnoreCase))
+            {
+                var result = await _channelManager.GetChannelsInternal(new ChannelQuery()
+                {
+                    UserId = user.Id.ToString("N")
+
+                }, CancellationToken.None).ConfigureAwait(false);
+
+                return result.Items;
+            }
+            if (string.Equals(folder.ViewType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
+            {
+                return SortItems(folder.GetChildren(user, true).OfType<Series>(), user, sort);
+            }
+            if (string.Equals(folder.ViewType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
+            {
+                return GetPlainFolderChildrenSorted(folder, user, sort);
+            }
+            if (string.Equals(folder.ViewType, CollectionType.Music, StringComparison.OrdinalIgnoreCase))
+            {
+                return SortItems(folder.GetChildren(user, true).OfType<MusicArtist>(), user, sort);
+            }
+
+            return GetPlainFolderChildrenSorted(folder, user, sort);
         }
 
-        private IEnumerable<BaseItem> GetChildrenSorted(Folder folder, User user, SortCriteria sort)
+        private IEnumerable<BaseItem> GetPlainFolderChildrenSorted(Folder folder, User user, SortCriteria sort)
         {
             var items = folder.GetChildren(user, true);
 
@@ -345,7 +456,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
         private IEnumerable<BaseItem> SortItems(IEnumerable<BaseItem> items, User user, SortCriteria sort)
         {
-            return _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
+            return _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, sort.SortOrder);
         }
 
         private IEnumerable<BaseItem> FilterUnsupportedContent(IEnumerable<BaseItem> items)
@@ -353,14 +464,12 @@ namespace MediaBrowser.Dlna.ContentDirectory
             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)
                 {
@@ -368,7 +477,6 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 }
 
                 // Upnp renderers won't understand these
-                // TODO: Display and prevent playback with restricted flag?
                 if (i is Game || i is Book)
                 {
                     return false;

+ 9 - 1
MediaBrowser.Dlna/Didl/DidlBuilder.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
@@ -612,6 +613,13 @@ namespace MediaBrowser.Dlna.Didl
             {
                 return GetImageInfo(item, ImageType.Thumb);
             }
+            if (item.HasImage(ImageType.Backdrop))
+            {
+                if (item is Channel)
+                {
+                    return GetImageInfo(item, ImageType.Backdrop);
+                }
+            }
 
             if (item is Audio || item is Episode)
             {
@@ -633,7 +641,7 @@ namespace MediaBrowser.Dlna.Didl
 
             try
             {
-                tag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary);
+                tag = _imageProcessor.GetImageCacheTag(item, type);
             }
             catch
             {

BIN
MediaBrowser.Dlna/Images/logo120.jpg


BIN
MediaBrowser.Dlna/Images/logo120.png


BIN
MediaBrowser.Dlna/Images/logo240.jpg


BIN
MediaBrowser.Dlna/Images/logo240.png


BIN
MediaBrowser.Dlna/Images/logo48.jpg


BIN
MediaBrowser.Dlna/Images/logo48.png


+ 4 - 0
MediaBrowser.Dlna/MediaBrowser.Dlna.csproj

@@ -184,6 +184,10 @@
     <EmbeddedResource Include="Profiles\Xml\Windows 8 RT.xml" />
     <EmbeddedResource Include="Profiles\Xml\Windows Phone.xml" />
   </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Images\logo240.jpg" />
+    <EmbeddedResource Include="Images\logo240.png" />
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.

+ 2 - 2
MediaBrowser.Dlna/PlayTo/Device.cs

@@ -424,7 +424,7 @@ namespace MediaBrowser.Dlna.PlayTo
             }
             catch (Exception ex)
             {
-                _logger.ErrorException("Error updating device info", ex);
+                _logger.ErrorException("Error updating device info for {0}", ex, Properties.Name);
 
                 _successiveStopCount++;
 
@@ -444,7 +444,7 @@ namespace MediaBrowser.Dlna.PlayTo
             }
             catch (Exception ex)
             {
-                _logger.ErrorException("Error updating device info", ex);
+                _logger.ErrorException("Error updating device volume info for {0}", ex, Properties.Name);
             }
         }
 

+ 37 - 30
MediaBrowser.Dlna/PlayTo/PlayToManager.cs

@@ -77,42 +77,44 @@ namespace MediaBrowser.Dlna.PlayTo
                 return;
             }
 
-            var uri = new Uri(location);
-
-            var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger).ConfigureAwait(false);
-            
-            if (device.RendererCommands != null)
+            try
             {
-                var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null)
-                    .ConfigureAwait(false);
+                var uri = new Uri(location);
 
-                var controller = sessionInfo.SessionController as PlayToController;
+                var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger).ConfigureAwait(false);
 
-                if (controller == null)
+                if (device.RendererCommands != null)
                 {
-                    var serverAddress = GetServerAddress(localIp);
+                    var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null)
+                        .ConfigureAwait(false);
 
-                    sessionInfo.SessionController = controller = new PlayToController(sessionInfo,
-                        _sessionManager,
-                        _itemRepository,
-                        _libraryManager,
-                        _logger,
-                        _dlnaManager,
-                        _userManager,
-                        _imageProcessor,
-                        serverAddress,
-                        _deviceDiscovery);
+                    var controller = sessionInfo.SessionController as PlayToController;
 
-                    controller.Init(device);
+                    if (controller == null)
+                    {
+                        var serverAddress = GetServerAddress(localIp);
 
-                    var profile = _dlnaManager.GetProfile(device.Properties.ToDeviceIdentification()) ??
-                                  _dlnaManager.GetDefaultProfile();
+                        sessionInfo.SessionController = controller = new PlayToController(sessionInfo,
+                            _sessionManager,
+                            _itemRepository,
+                            _libraryManager,
+                            _logger,
+                            _dlnaManager,
+                            _userManager,
+                            _imageProcessor,
+                            serverAddress,
+                            _deviceDiscovery);
 
-                    _sessionManager.ReportCapabilities(sessionInfo.Id, new SessionCapabilities
-                    {
-                        PlayableMediaTypes = profile.GetSupportedMediaTypes(),
+                        controller.Init(device);
+
+                        var profile = _dlnaManager.GetProfile(device.Properties.ToDeviceIdentification()) ??
+                                      _dlnaManager.GetDefaultProfile();
 
-                        SupportedCommands = new List<string>
+                        _sessionManager.ReportCapabilities(sessionInfo.Id, new SessionCapabilities
+                        {
+                            PlayableMediaTypes = profile.GetSupportedMediaTypes(),
+
+                            SupportedCommands = new List<string>
                         {
                             GeneralCommandType.VolumeDown.ToString(),
                             GeneralCommandType.VolumeUp.ToString(),
@@ -124,12 +126,17 @@ namespace MediaBrowser.Dlna.PlayTo
                             GeneralCommandType.SetSubtitleStreamIndex.ToString()
                         },
 
-                        SupportsMediaControl = true
-                    });
+                            SupportsMediaControl = true
+                        });
 
-                    _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
+                        _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
+                    }
                 }
             }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error creating PlayTo device.", ex);
+            }
         }
 
         private string GetServerAddress(IPAddress localIp)

+ 2 - 0
MediaBrowser.Dlna/Profiles/DefaultProfile.cs

@@ -30,6 +30,8 @@ namespace MediaBrowser.Dlna.Profiles
             MaxStreamingBitrate = 8000000;
             MaxStaticBitrate = 8000000;
 
+            EnableAlbumArtInDidl = true;
+
             TranscodingProfiles = new[]
             {
                 new TranscodingProfile

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Android.xml

@@ -9,7 +9,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Default.xml

@@ -9,7 +9,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml

@@ -14,7 +14,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml

@@ -15,7 +15,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml

@@ -13,7 +13,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml

@@ -15,7 +15,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml

@@ -16,7 +16,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml

@@ -15,7 +15,7 @@
   <ModelNumber>3.0</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml

@@ -17,7 +17,7 @@
   <ModelNumber>3.0</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml

@@ -16,7 +16,7 @@
   <ModelNumber>3.0</ModelNumber>
   <ModelUrl>http://www.microsoft.com/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_TN</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml

@@ -16,7 +16,7 @@
   <ModelNumber>3.0</ModelNumber>
   <ModelUrl>http://www.microsoft.com/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_TN</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml

@@ -16,7 +16,7 @@
   <ModelNumber>3.0</ModelNumber>
   <ModelUrl>http://www.microsoft.com/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_TN</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml

@@ -16,7 +16,7 @@
   <ModelNumber>3.0</ModelNumber>
   <ModelUrl>http://www.microsoft.com/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_TN</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml

@@ -16,7 +16,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_TN</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml

@@ -16,7 +16,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>true</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml

@@ -13,7 +13,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml

@@ -9,7 +9,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml

@@ -16,7 +16,7 @@
   <ModelNumber>12.0</ModelNumber>
   <ModelUrl>http://www.microsoft.com/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml

@@ -14,7 +14,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml

@@ -15,7 +15,7 @@
   <ModelNumber>Media Browser</ModelNumber>
   <ModelUrl>http://mediabrowser.tv/</ModelUrl>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
   <SupportedMediaTypes>Audio</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
   <MaxAlbumArtWidth>512</MaxAlbumArtWidth>

+ 18 - 0
MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs

@@ -133,6 +133,24 @@ namespace MediaBrowser.Dlna.Server
         {
             var list = new List<DeviceIcon>();
 
+            list.Add(new DeviceIcon
+            {
+                MimeType = "image/png",
+                Depth = "24",
+                Width = 240,
+                Height = 240,
+                Url = "/mediabrowser/dlna/icons/logo240.png"
+            });
+
+            list.Add(new DeviceIcon
+            {
+                MimeType = "image/jpeg",
+                Depth = "24",
+                Width = 240,
+                Height = 240,
+                Url = "/mediabrowser/dlna/icons/logo240.jpg"
+            });
+
             list.Add(new DeviceIcon
             {
                 MimeType = "image/png",

+ 7 - 2
MediaBrowser.Dlna/Ssdp/SsdpHandler.cs

@@ -90,9 +90,14 @@ namespace MediaBrowser.Dlna.Ssdp
 
             values["HOST"] = "239.255.255.250:1900";
             values["USER-AGENT"] = "UPnP/1.0 DLNADOC/1.50 Platinum/1.0.4.2";
-            values["ST"] = "ssdp:all";
+
             values["MAN"] = "\"ssdp:discover\"";
-            values["MX"] = "10";
+
+            // Search target
+            values["ST"] = "ssdp:all";
+
+            // Seconds to delay response
+            values["MX"] = "3";
 
             SendDatagram("M-SEARCH * HTTP/1.1", values, localIp);
         }

+ 7 - 1
MediaBrowser.Model/Dlna/SortCriteria.cs

@@ -1,8 +1,14 @@
-
+using MediaBrowser.Model.Entities;
+
 namespace MediaBrowser.Model.Dlna
 {
     public class SortCriteria
     {
+        public SortOrder SortOrder
+        {
+            get { return SortOrder.Ascending; }
+        }
+
         public SortCriteria(string value)
         {
             

+ 1 - 1
MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs

@@ -71,7 +71,7 @@ namespace MediaBrowser.Server.Implementations.Channels
                 var startingPercent = numComplete * percentPerUser * 100;
 
                 var innerProgress = new ActionableProgress<double>();
-                innerProgress.RegisterAction(p => progress.Report(startingPercent + (.8 * p)));
+                innerProgress.RegisterAction(p => progress.Report(startingPercent + (percentPerUser * p)));
 
                 await DownloadContent(user, cancellationToken, innerProgress).ConfigureAwait(false);
 

+ 58 - 9
MediaBrowser.Server.Implementations/Channels/ChannelManager.cs

@@ -106,7 +106,7 @@ namespace MediaBrowser.Server.Implementations.Channels
                 .OrderBy(i => i.Name);
         }
 
-        public Task<QueryResult<BaseItemDto>> GetChannels(ChannelQuery query, CancellationToken cancellationToken)
+        public Task<QueryResult<Channel>> GetChannelsInternal(ChannelQuery query, CancellationToken cancellationToken)
         {
             var user = string.IsNullOrWhiteSpace(query.UserId)
                 ? null
@@ -148,21 +148,40 @@ namespace MediaBrowser.Server.Implementations.Channels
                 all = all.Take(query.Limit.Value).ToList();
             }
 
+            var returnItems = all.ToArray();
+
+            var result = new QueryResult<Channel>
+            {
+                Items = returnItems,
+                TotalRecordCount = totalCount
+            };
+
+            return Task.FromResult(result);
+        }
+
+        public async Task<QueryResult<BaseItemDto>> GetChannels(ChannelQuery query, CancellationToken cancellationToken)
+        {
+            var user = string.IsNullOrWhiteSpace(query.UserId)
+                ? null
+                : _userManager.GetUserById(new Guid(query.UserId));
+
+            var internalResult = await GetChannelsInternal(query, cancellationToken).ConfigureAwait(false);
+
             // Get everything
             var fields = Enum.GetNames(typeof(ItemFields))
                     .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
                     .ToList();
 
-            var returnItems = all.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
+            var returnItems = internalResult.Items.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                 .ToArray();
 
             var result = new QueryResult<BaseItemDto>
             {
                 Items = returnItems,
-                TotalRecordCount = totalCount
+                TotalRecordCount = internalResult.TotalRecordCount
             };
 
-            return Task.FromResult(result);
+            return result;
         }
 
         public async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
@@ -846,7 +865,7 @@ namespace MediaBrowser.Server.Implementations.Channels
             }
         }
 
-        public async Task<QueryResult<BaseItemDto>> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken)
+        public async Task<QueryResult<BaseItem>> GetChannelItemsInternal(ChannelItemQuery query, CancellationToken cancellationToken)
         {
             // Get the internal channel entity
             var channel = GetChannel(query.ChannelId);
@@ -868,6 +887,12 @@ namespace MediaBrowser.Server.Implementations.Channels
                     throw new ArgumentException(string.Format("{0} channel only supports a maximum of {1} records at a time.", channel.Name, channelInfo.MaxPageSize.Value));
                 }
                 providerLimit = query.Limit;
+
+                // This will cause some providers to fail
+                if (providerLimit == 0)
+                {
+                    providerLimit = 1;
+                }
             }
 
             var user = string.IsNullOrWhiteSpace(query.UserId)
@@ -913,6 +938,31 @@ namespace MediaBrowser.Server.Implementations.Channels
             return await GetReturnItems(internalItems, providerTotalRecordCount, user, query, cancellationToken).ConfigureAwait(false);
         }
 
+        public async Task<QueryResult<BaseItemDto>> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken)
+        {
+            var user = string.IsNullOrWhiteSpace(query.UserId)
+                ? null
+                : _userManager.GetUserById(new Guid(query.UserId));
+
+            var internalResult = await GetChannelItemsInternal(query, cancellationToken).ConfigureAwait(false);
+
+            // Get everything
+            var fields = Enum.GetNames(typeof(ItemFields))
+                    .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
+                    .ToList();
+
+            var returnItems = internalResult.Items.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
+                .ToArray();
+
+            var result = new QueryResult<BaseItemDto>
+            {
+                Items = returnItems,
+                TotalRecordCount = internalResult.TotalRecordCount
+            };
+
+            return result;
+        }
+
         private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
         private async Task<ChannelItemResult> GetChannelItems(IChannel channel,
             User user,
@@ -1054,7 +1104,7 @@ namespace MediaBrowser.Server.Implementations.Channels
                 filename + ".json");
         }
 
-        private async Task<QueryResult<BaseItemDto>> GetReturnItems(IEnumerable<BaseItem> items, int? totalCountFromProvider, User user, ChannelItemQuery query, CancellationToken cancellationToken)
+        private async Task<QueryResult<BaseItem>> GetReturnItems(IEnumerable<BaseItem> items, int? totalCountFromProvider, User user, ChannelItemQuery query, CancellationToken cancellationToken)
         {
             items = ApplyFilters(items, query.Filters, user);
 
@@ -1078,10 +1128,9 @@ namespace MediaBrowser.Server.Implementations.Channels
 
             await RefreshIfNeeded(all, cancellationToken).ConfigureAwait(false);
 
-            var returnItemArray = all.Select(i => _dtoService.GetBaseItemDto(i, query.Fields, user))
-                .ToArray();
+            var returnItemArray = all.ToArray();
 
-            return new QueryResult<BaseItemDto>
+            return new QueryResult<BaseItem>
             {
                 Items = returnItemArray,
                 TotalRecordCount = totalCount

+ 0 - 5
MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs

@@ -97,11 +97,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
             var info = _appHost.GetSystemInfo();
 
             CreatePortMap(device, info.HttpServerPortNumber);
-
-            if (info.WebSocketPortNumber != info.HttpServerPortNumber)
-            {
-                CreatePortMap(device, info.WebSocketPortNumber);
-            }
         }
 
         private void CreatePortMap(INatDevice device, int port)

+ 5 - 2
MediaBrowser.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
 using MediaBrowser.Server.Implementations.Udp;
 using System.Net.Sockets;
 
@@ -37,6 +38,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
         /// </summary>
         private readonly IHttpServer _httpServer;
         private readonly IServerApplicationHost _appHost;
+        private readonly IJsonSerializer _json;
 
         public const int PortNumber = 7359;
 
@@ -47,13 +49,14 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
         /// <param name="networkManager">The network manager.</param>
         /// <param name="serverConfigurationManager">The server configuration manager.</param>
         /// <param name="httpServer">The HTTP server.</param>
-        public UdpServerEntryPoint(ILogger logger, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager, IHttpServer httpServer, IServerApplicationHost appHost)
+        public UdpServerEntryPoint(ILogger logger, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager, IHttpServer httpServer, IServerApplicationHost appHost, IJsonSerializer json)
         {
             _logger = logger;
             _networkManager = networkManager;
             _serverConfigurationManager = serverConfigurationManager;
             _httpServer = httpServer;
             _appHost = appHost;
+            _json = json;
         }
 
         /// <summary>
@@ -61,7 +64,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
         /// </summary>
         public void Run()
         {
-            var udpServer = new UdpServer(_logger, _networkManager, _serverConfigurationManager, _httpServer, _appHost);
+            var udpServer = new UdpServer(_logger, _networkManager, _serverConfigurationManager, _httpServer, _appHost, _json);
 
             try
             {

+ 27 - 16
MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs

@@ -59,14 +59,16 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
 
             var collectionType = args.GetCollectionType();
 
+            var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music,
+                StringComparison.OrdinalIgnoreCase);
+
             // If there's a collection type and it's not music, don't allow it.
-            if (!string.IsNullOrEmpty(collectionType) &&
-                !string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase))
+            if (!isMusicMediaFolder)
             {
                 return null;
             }
 
-            return IsMusicAlbum(args) ? new MusicAlbum() : null;
+            return IsMusicAlbum(args, isMusicMediaFolder) ? new MusicAlbum() : null;
         }
 
 
@@ -74,27 +76,29 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
         /// Determine if the supplied file data points to a music album
         /// </summary>
         /// <param name="path">The path.</param>
+        /// <param name="isMusicMediaFolder">if set to <c>true</c> [is music media folder].</param>
         /// <param name="directoryService">The directory service.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="fileSystem">The file system.</param>
         /// <returns><c>true</c> if [is music album] [the specified data]; otherwise, <c>false</c>.</returns>
-        public static bool IsMusicAlbum(string path, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem)
+        public static bool IsMusicAlbum(string path, bool isMusicMediaFolder, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem)
         {
-            return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, logger, fileSystem);
+            return ContainsMusic(directoryService.GetFileSystemEntries(path), isMusicMediaFolder, true, directoryService, logger, fileSystem);
         }
 
         /// <summary>
         /// Determine if the supplied resolve args should be considered a music album
         /// </summary>
         /// <param name="args">The args.</param>
+        /// <param name="isMusicMediaFolder">if set to <c>true</c> [is music media folder].</param>
         /// <returns><c>true</c> if [is music album] [the specified args]; otherwise, <c>false</c>.</returns>
-        private bool IsMusicAlbum(ItemResolveArgs args)
+        private bool IsMusicAlbum(ItemResolveArgs args, bool isMusicMediaFolder)
         {
             // Args points to an album if parent is an Artist folder or it directly contains music
             if (args.IsDirectory)
             {
                 //if (args.Parent is MusicArtist) return true;  //saves us from testing children twice
-                if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem)) return true;
+                if (ContainsMusic(args.FileSystemChildren, isMusicMediaFolder, true, args.DirectoryService, _logger, _fileSystem)) return true;
             }
 
             return false;
@@ -104,12 +108,18 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
         /// Determine if the supplied list contains what we should consider music
         /// </summary>
         /// <param name="list">The list.</param>
+        /// <param name="isMusicMediaFolder">if set to <c>true</c> [is music media folder].</param>
         /// <param name="allowSubfolders">if set to <c>true</c> [allow subfolders].</param>
         /// <param name="directoryService">The directory service.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="fileSystem">The file system.</param>
         /// <returns><c>true</c> if the specified list contains music; otherwise, <c>false</c>.</returns>
-        private static bool ContainsMusic(IEnumerable<FileSystemInfo> list, bool allowSubfolders, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem)
+        private static bool ContainsMusic(IEnumerable<FileSystemInfo> list, 
+            bool isMusicMediaFolder,
+            bool allowSubfolders, 
+            IDirectoryService directoryService, 
+            ILogger logger, 
+            IFileSystem fileSystem)
         {
             // If list contains at least 2 audio files or at least one and no video files consider it to contain music
             var foundAudio = 0;
@@ -120,7 +130,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
             {
                 if ((fileSystemInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                 {
-                    if (allowSubfolders && IsAlbumSubfolder(fileSystemInfo, directoryService, logger, fileSystem))
+                    if (isMusicMediaFolder && allowSubfolders && IsAlbumSubfolder(fileSystemInfo, true, directoryService, logger, fileSystem))
                     {
                         discSubfolderCount++;
                     }
@@ -135,27 +145,27 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
                 if (EntityResolutionHelper.IsAudioFile(fullName))
                 {
                     // Don't resolve these into audio files
-                    if (string.Equals(fileSystem.GetFileNameWithoutExtension(fullName), BaseItem.ThemeSongFilename) && EntityResolutionHelper.IsAudioFile(fullName))
+                    if (string.Equals(fileSystem.GetFileNameWithoutExtension(fullName), BaseItem.ThemeSongFilename))
                     {
                         continue;
                     }
 
                     foundAudio++;
                 }
+                else if (EntityResolutionHelper.IsVideoFile(fullName)) return false;
+                else if (EntityResolutionHelper.IsVideoPlaceHolder(fullName)) return false;
+                
                 if (foundAudio >= 2)
                 {
                     return true;
                 }
-
-                if (EntityResolutionHelper.IsVideoFile(fullName)) return false;
-                if (EntityResolutionHelper.IsVideoPlaceHolder(fullName)) return false;
             }
 
             //  or a single audio file and no video files
             return foundAudio > 0 || discSubfolderCount > 0;
         }
 
-        private static bool IsAlbumSubfolder(FileSystemInfo directory, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem)
+        private static bool IsAlbumSubfolder(FileSystemInfo directory, bool isMusicMediaFolder, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem)
         {
             var path = directory.FullName;
 
@@ -163,7 +173,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
             {
                 logger.Debug("Found multi-disc folder: " + path);
 
-                return ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem);
+                return ContainsMusic(directoryService.GetFileSystemEntries(path), isMusicMediaFolder, false, directoryService, logger, fileSystem);
             }
 
             return false;
@@ -171,7 +181,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
 
         private static bool IsMultiDiscFolder(string path)
         {
-            return EntityResolutionHelper.IsMultiPartFolder(path);
+            return false;
+            //return EntityResolutionHelper.IsMultiPartFolder(path);
         }
 
         private static bool IsAdditionalSubfolderAllowed(FileSystemInfo directory)

+ 5 - 3
MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs

@@ -62,9 +62,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
 
             var collectionType = args.GetCollectionType();
 
+            var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music,
+                StringComparison.OrdinalIgnoreCase);
+
             // If there's a collection type and it's not music, it can't be a series
-            if (!string.IsNullOrEmpty(collectionType) &&
-                !string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase))
+            if (!isMusicMediaFolder)
             {
                 return null;
             }
@@ -72,7 +74,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
             var directoryService = args.DirectoryService;
             
             // If we contain an album assume we are an artist folder
-            return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => MusicAlbumResolver.IsMusicAlbum(i.FullName, directoryService, _logger, _fileSystem)) ? new MusicArtist() : null;
+            return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => MusicAlbumResolver.IsMusicAlbum(i.FullName, isMusicMediaFolder, directoryService, _logger, _fileSystem)) ? new MusicArtist() : null;
         }
 
     }

+ 5 - 13
MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs

@@ -5,6 +5,7 @@ using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Resolvers;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
 using System;
 using System.IO;
 
@@ -16,10 +17,12 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
     public class SeriesResolver : FolderResolver<Series>
     {
         private readonly IFileSystem _fileSystem;
+        private readonly ILogger _logger;
 
-        public SeriesResolver(IFileSystem fileSystem)
+        public SeriesResolver(IFileSystem fileSystem, ILogger logger)
         {
             _fileSystem = fileSystem;
+            _logger = logger;
         }
 
         /// <summary>
@@ -64,19 +67,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
                 {
                     return null;
                 }
-                
-                // It's a Series if any of the following conditions are met:
-                // series.xml exists
-                // [tvdbid= is present in the path
-                // TVUtils.IsSeriesFolder returns true
-                var filename = Path.GetFileName(args.Path);
-
-                if (string.IsNullOrEmpty(filename))
-                {
-                    return null;
-                }
 
-                if (TVUtils.IsSeriesFolder(args.Path, collectionType == CollectionType.TvShows, args.FileSystemChildren, args.DirectoryService, _fileSystem))
+                if (TVUtils.IsSeriesFolder(args.Path, string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase), args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger))
                 {
                     return new Series();
                 }

+ 40 - 2
MediaBrowser.Server.Implementations/Library/UserViewManager.cs

@@ -1,4 +1,7 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
@@ -16,6 +19,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Server.Implementations.Configuration;
 
 namespace MediaBrowser.Server.Implementations.Library
 {
@@ -28,8 +32,9 @@ namespace MediaBrowser.Server.Implementations.Library
 
         private readonly IChannelManager _channelManager;
         private readonly ILiveTvManager _liveTvManager;
+        private readonly IServerApplicationPaths _appPaths;
 
-        public UserViewManager(ILibraryManager libraryManager, ILocalizationManager localizationManager, IFileSystem fileSystem, IUserManager userManager, IChannelManager channelManager, ILiveTvManager liveTvManager)
+        public UserViewManager(ILibraryManager libraryManager, ILocalizationManager localizationManager, IFileSystem fileSystem, IUserManager userManager, IChannelManager channelManager, ILiveTvManager liveTvManager, IServerApplicationPaths appPaths)
         {
             _libraryManager = libraryManager;
             _localizationManager = localizationManager;
@@ -37,6 +42,7 @@ namespace MediaBrowser.Server.Implementations.Library
             _userManager = userManager;
             _channelManager = channelManager;
             _liveTvManager = liveTvManager;
+            _appPaths = appPaths;
         }
 
         public async Task<IEnumerable<Folder>> GetUserViews(UserViewQuery query, CancellationToken cancellationToken)
@@ -124,5 +130,37 @@ namespace MediaBrowser.Server.Implementations.Library
 
             return _libraryManager.GetNamedView(name, type, sortName, cancellationToken);
         }
+
+        public async Task<SpecialFolder> GetSpecialFolder(string name, SpecialFolderType type, string itemType, CancellationToken cancellationToken)
+        {
+            var path = Path.Combine(_appPaths.ItemsByNamePath,
+                "specialfolders",
+                _fileSystem.GetValidFilename(name));
+
+            var id = (path + "_specialfolder_" + name).GetMBId(typeof(SpecialFolder));
+
+            var item = _libraryManager.GetItemById(id) as SpecialFolder;
+
+            if (item == null)
+            {
+                Directory.CreateDirectory(Path.GetDirectoryName(path));
+
+                item = new SpecialFolder
+                {
+                    Path = path,
+                    Id = id,
+                    DateCreated = DateTime.UtcNow,
+                    Name = name,
+                    SpecialFolderType = type,
+                    ItemTypeName = itemType
+                };
+
+                await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
+
+                await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+            }
+
+            return item;
+        }
     }
 }

+ 1 - 1
MediaBrowser.Server.Implementations/LiveTv/CleanDatabaseScheduledTask.cs

@@ -41,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
         {
             return new ITaskTrigger[] 
             { 
-                new IntervalTrigger{ Interval = TimeSpan.FromHours(24)}
+                new IntervalTrigger{ Interval = TimeSpan.FromHours(12)}
             };
         }
 

+ 0 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json

@@ -178,7 +178,6 @@
     "LabelVideoCodec": "Video: {0}",
     "LabelRemoteAccessUrl": "Remote access: {0}",
     "LabelRunningOnPort": "Running on port {0}.",
-    "LabelRunningOnPorts": "Running on ports {0} and {1}.",
     "HeaderLatestFromChannel": "Latest from {0}",
     "ButtonDownload": "Download",
     "LabelUnknownLanaguage": "Unknown language",

+ 4 - 4
MediaBrowser.Server.Implementations/Localization/JavaScript/kk.json

@@ -1,5 +1,5 @@
 {
-    "SettingsSaved": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0441\u0430\u049b\u0442\u0430\u043b\u0434\u044b.",
+    "SettingsSaved": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043b\u0435\u0440 \u0441\u0430\u049b\u0442\u0430\u043b\u0434\u044b.",
     "AddUser": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b\u043d\u044b \u04af\u0441\u0442\u0435\u0443",
     "Users": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b\u043b\u0430\u0440",
     "Delete": "\u0416\u043e\u044e",
@@ -46,9 +46,9 @@
     "HeaderTaskTriggers": "\u0422\u0430\u043f\u0441\u044b\u0440\u043c\u0430 \u0442\u0440\u0438\u0433\u0433\u0435\u0440\u043b\u0435\u0440\u0456",
     "MessageDeleteTaskTrigger": "\u0428\u044b\u043d\u044b\u043c\u0435\u043d \u043e\u0441\u044b \u0442\u0430\u043f\u0441\u044b\u0440\u043c\u0430 \u0442\u0440\u0438\u0433\u0433\u0435\u0440\u0456\u043d \u0436\u043e\u044e \u049b\u0430\u0436\u0435\u0442 \u043f\u0435?",
     "MessageNoPluginsInstalled": "\u041e\u0440\u043d\u0430\u0442\u044b\u043b\u0493\u0430\u043d \u043f\u043b\u0430\u0433\u0438\u043d\u0434\u0435\u0440 \u0436\u043e\u049b.",
-    "LabelVersionInstalled": "{0} \u043d\u04b1\u0441\u049b\u0430\u0441\u044b \u043e\u0440\u043d\u0430\u0442\u044b\u043b\u0493\u0430\u043d",
+    "LabelVersionInstalled": "{0} \u043e\u0440\u043d\u0430\u0442\u044b\u043b\u0493\u0430\u043d",
     "LabelNumberReviews": "{0} \u043f\u0456\u043a\u0456\u0440",
-    "LabelFree": "\u0410\u049b\u044b\u0441\u044b\u0437",
+    "LabelFree": "\u0422\u0435\u0433\u0456\u043d",
     "HeaderSelectAudio": "\u0414\u044b\u0431\u044b\u0441 \u0442\u0430\u04a3\u0434\u0430\u0443",
     "HeaderSelectSubtitles": "\u0421\u0443\u0431\u0442\u0438\u0442\u0440\u043b\u0435\u0440 \u0442\u0430\u04a3\u0434\u0430\u0443",
     "LabelDefaultStream": "(\u04d8\u0434\u0435\u043f\u043a\u0456)",
@@ -289,7 +289,7 @@
     "ButtonRestart": "\u049a\u0430\u0439\u0442\u0430 \u0456\u0441\u043a\u0435 \u049b\u043e\u0441\u0443",
     "MessagePleaseRefreshPage": "\u0416\u0430\u04a3\u0430 \u0436\u0430\u04a3\u0430\u0440\u0442\u0443\u043b\u0430\u0440\u0434\u044b \u0430\u043b\u0443 \u04af\u0448\u0456\u043d \u043e\u0441\u044b \u0431\u0435\u0442\u0442\u0456 \u049b\u0430\u0439\u0442\u0430 \u0436\u04af\u043a\u0442\u0435\u04a3\u0456\u0437.",
     "ButtonHide": "\u0416\u0430\u0441\u044b\u0440\u0443",
-    "MessageSettingsSaved": "\u0422\u0435\u04a3\u0448\u0435\u043b\u0456\u043c \u0441\u0430\u049b\u0442\u0430\u043b\u0434\u044b.",
+    "MessageSettingsSaved": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043b\u0435\u0440 \u0441\u0430\u049b\u0442\u0430\u043b\u0434\u044b.",
     "ButtonSignOut": "\u0428\u044b\u0493\u0443",
     "ButtonMyProfile": "\u041c\u0435\u043d\u0456\u04a3 \u043f\u0440\u043e\u0444\u0430\u0439\u043b\u044b\u043c",
     "ButtonMyPreferences": "\u041c\u0435\u043d\u0456\u04a3 \u0442\u0435\u04a3\u0448\u0435\u043b\u0456\u043c\u0434\u0435\u0440\u0456\u043c",

+ 1 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json

@@ -314,7 +314,7 @@
     "ButtonSubtitles": "Legendas",
     "ButtonScenes": "Cenas",
     "ButtonQuality": "Quality",
-    "HeaderNotifications": "Notifications",
+    "HeaderNotifications": "Notifica\u00e7\u00f5es",
     "HeaderSelectPlayer": "Select Player:",
     "ButtonSelect": "Selecionar",
     "ButtonNew": "Novo",

+ 3 - 3
MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json

@@ -46,8 +46,8 @@
     "HeaderTaskTriggers": "\u0422\u0440\u0438\u0433\u0433\u0435\u0440\u044b \u0437\u0430\u0434\u0430\u043d\u0438\u044f",
     "MessageDeleteTaskTrigger": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0439 \u0442\u0440\u0438\u0433\u0433\u0435\u0440 \u0437\u0430\u0434\u0430\u043d\u0438\u044f?",
     "MessageNoPluginsInstalled": "\u041d\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430.",
-    "LabelVersionInstalled": "\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f: {0}",
-    "LabelNumberReviews": "\u041e\u0442\u0437\u044b\u0432\u043e\u0432: {0}",
+    "LabelVersionInstalled": "\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430: {0}",
+    "LabelNumberReviews": "\u041e\u0442\u0437\u044b\u0432\u044b: {0}",
     "LabelFree": "\u0411\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e",
     "HeaderSelectAudio": "\u0412\u044b\u0431\u043e\u0440 \u0430\u0443\u0434\u0438\u043e",
     "HeaderSelectSubtitles": "\u0412\u044b\u0431\u043e\u0440 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u043e\u0432",
@@ -232,7 +232,7 @@
     "OptionBlockChannelContent": "\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u043a\u0430\u043d\u0430\u043b\u043e\u0432",
     "ButtonRevoke": "\u041e\u0442\u043e\u0437\u0432\u0430\u0442\u044c",
     "MessageConfirmRevokeApiKey": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0442\u043e\u0437\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API?  \u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043a Media Browser \u0431\u0443\u0434\u0435\u0442 \u0440\u0435\u0437\u043a\u043e \u043e\u0431\u043e\u0440\u0432\u0430\u043d\u043e.",
-    "HeaderConfirmRevokeApiKey": "\u041e\u0442\u0437\u044b\u0432 \u043a\u043b\u044e\u0447\u0430 API",
+    "HeaderConfirmRevokeApiKey": "\u041e\u0442\u0437\u044b\u0432\u0430\u043d\u0438\u0435 \u043a\u043b\u044e\u0447\u0430 API",
     "ValueContainer": "\u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440: {0}",
     "ValueAudioCodec": "\u0410\u0443\u0434\u0438\u043e \u043a\u043e\u0434\u0435\u043a: {0}",
     "ValueVideoCodec": "\u0412\u0438\u0434\u0435\u043e \u043a\u043e\u0434\u0435\u043a: {0}",

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/ar.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/ca.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/cs.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/da.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "System venligt navn",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/de.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Maximale Bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Freundlicher Name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/el.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/en_GB.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/en_US.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/es.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "Estos valores controlan el modo en que Media Browser se presentar\u00e1 en el dispositivo.",
     "LabelMaxBitrate": "Bitrate m\u00e1ximo:",
     "LabelMaxBitrateHelp": "Especificar una tasa de bits m\u00e1xima en entornos de ancho de banda limitado, o si el dispositivo impone su propio l\u00edmite.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignorar las solicitudes de intervalo de bytes de transcodificaci\u00f3n",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "Si est\u00e1 activado, estas solicitudes ser\u00e1n atendidas pero ignorar\u00e1n el encabezado de intervalo de bytes.",
     "LabelFriendlyName": "Nombre amigable",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 13 - 1
MediaBrowser.Server.Implementations/Localization/Server/es_MX.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "Estos valores controlan la manera en que Media Browser se presentar\u00e1 a s\u00ed mismo ante el dispositivo.",
     "LabelMaxBitrate": "Tasa de bits m\u00e1xima:",
     "LabelMaxBitrateHelp": "Especifique la tasa de bits m\u00e1xima para ambientes con un ancho de banda limitado, o si el dispositivo impone sus propios l\u00edmites.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignorar solicitudes de transcodificaci\u00f3n de rango de byte.",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "Si se habilita, estas solicitudes seran honradas pero se ignorar\u00e1 el encabezado de rango de byte.",
     "LabelFriendlyName": "Nombre amistoso:",
@@ -888,5 +892,13 @@
     "ButtonSync": "Sincronizar",
     "TabScheduledTasks": "Tareas Programadas",
     "HeaderChapters": "Cap\u00edtulos",
-    "HeaderResumeSettings": "Configuraci\u00f3n para Continuar"
+    "HeaderResumeSettings": "Configuraci\u00f3n para Continuar",
+    "TabSync": "Sync",
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/fr.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "Ces valeurs contr\u00f4lent comment Media Browser sera pr\u00e9sent\u00e9 \u00e0 l'appareil.",
     "LabelMaxBitrate": "D\u00e9bit maximum:",
     "LabelMaxBitrateHelp": "Sp\u00e9cifiez un d\u00e9bit maximum dans les environnements avec bande passante limit\u00e9e ou si l'appareil impose sa propre limite.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore le transcodage des demandes de plage d'octets",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "Si activ\u00e9, ces requ\u00eates\/demandes seront honor\u00e9es mais l'ent\u00eate de plage d'octets sera ignor\u00e9. ",
     "LabelFriendlyName": "Surnom d'affichage",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapitres",
     "HeaderResumeSettings": "Reprendre les param\u00e8tres",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/he.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/it.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "Controllano questi valori come Media Browser si presenter\u00e0 al dispositivo.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specificare un bitrate massimo in ambienti larghezza di banda limitata, o se il dispositivo impone il suo limite proprio.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignorare le richieste di intervallo di byte di trascodifica",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "Se abilitata, queste richieste saranno onorati, ma ignorano l'intestazione intervallo di byte.",
     "LabelFriendlyName": "Nome Condiviso",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

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


+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/ko.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/ms.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/nb.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 28 - 18
MediaBrowser.Server.Implementations/Localization/Server/nl.json

@@ -65,12 +65,12 @@
     "LabelSubtitleLanguagePreference": "Voorkeurs taal ondertiteling:",
     "OptionDefaultSubtitles": "Standaard",
     "OptionOnlyForcedSubtitles": "Alleen 'geforceerde' ondertiteling",
-    "OptionAlwaysPlaySubtitles": "Altijd ondertiteling weergeven",
+    "OptionAlwaysPlaySubtitles": "Ondertiteling altijd weergeven",
     "OptionNoSubtitles": "Geen ondertitels",
-    "OptionDefaultSubtitlesHelp": "Ondertiteling wordt weergegeven in de voorkeurstaal als de audio in een buitenlandse taal is.",
-    "OptionOnlyForcedSubtitlesHelp": "Alleen ondertiteling die als 'forced' gemarkeerd zijn worden geladen.",
+    "OptionDefaultSubtitlesHelp": "Ondertiteling wordt weergegeven in de voorkeurstaal als de audio in een andere taal is.",
+    "OptionOnlyForcedSubtitlesHelp": "Alleen ondertitels gemarkeerd als \"gedwongen\" zullen worden geladen.",
     "OptionAlwaysPlaySubtitlesHelp": "Ondertiteling wordt weergegeveen in de voorkeurstaal ongeacht de taal van de audio.",
-    "OptionNoSubtitlesHelp": "Ondertiteling wordt niet standaard weergegeven.",
+    "OptionNoSubtitlesHelp": "Ondertiteling wordt standaard niet weergegeven.",
     "TabProfiles": "Profielen",
     "TabSecurity": "Beveiliging",
     "ButtonAddUser": "Gebruiker toevoegen",
@@ -136,7 +136,7 @@
     "OptionAlbumArtist": "Albumartiest",
     "OptionArtist": "Artiest",
     "OptionAlbum": "Album",
-    "OptionTrackName": "Track Naam",
+    "OptionTrackName": "Naam van Nummer",
     "OptionCommunityRating": "Gemeenschaps Waardering",
     "OptionNameSort": "Naam",
     "OptionFolderSort": "Mappen",
@@ -237,7 +237,7 @@
     "ButtonGroupVersions": "Groepeer Versies",
     "ButtonAddToCollection": "Toevoegen aan verzameling",
     "PismoMessage": "Pismo File Mount (met een geschonken licentie).",
-    "TangibleSoftwareMessage": "Tangible Solution Java\/C# converters worden onder een gedoneerde licentie gebruikt.",
+    "TangibleSoftwareMessage": "Gebruik makend van concrete oplossingen als Java \/ C converters door een geschonken licentie.",
     "HeaderCredits": "Credits",
     "PleaseSupportOtherProduces": "Steun A.U.B. ook de andere gratis producten die wij gebruiken:",
     "VersionNumber": "Versie {0}",
@@ -296,7 +296,7 @@
     "PasswordLocalhostMessage": "Wachtwoorden zijn niet vereist bij het aanmelden van localhost.",
     "TabGuide": "Gids",
     "TabChannels": "Kanalen",
-    "TabCollections": "Verzameling",
+    "TabCollections": "Verzamelingen",
     "HeaderChannels": "Kanalen",
     "TabRecordings": "Opnamen",
     "TabScheduled": "Gepland",
@@ -613,7 +613,7 @@
     "ButtonArrowRight": "Rechts",
     "ButtonBack": "Terug",
     "ButtonInfo": "Info",
-    "ButtonOsd": "On Screen Display",
+    "ButtonOsd": "Weergave op het scherm",
     "ButtonPageUp": "Page Up",
     "ButtonPageDown": "Page Down",
     "PageAbbreviation": "PG",
@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "Deze waarden bepalen hoe Media Browser zichzelf zal presenteren aan het apparaat.",
     "LabelMaxBitrate": "Max. bitrate:",
     "LabelMaxBitrateHelp": "Geef een max. bitrate in bandbreedte beperkte omgevingen, of als het apparaat zijn eigen limiet heeft.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Transcodeer byte range-aanvragen negeren",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "Indien ingeschakeld, zal deze verzoeken worden gehonoreerd, maar zal de byte bereik header worden genegerd.",
     "LabelFriendlyName": "Aangepaste naam",
@@ -745,14 +749,14 @@
     "LabelDisplayPluginsFor": "Toon Plug-ins voor:",
     "PluginTabMediaBrowserClassic": "MB Classic",
     "PluginTabMediaBrowserTheater": "MB Theater",
-    "LabelEpisodeName": "Naam aflevering",
-    "LabelSeriesName": "Naam serie",
-    "ValueSeriesNamePeriod": "Naam.Serie",
-    "ValueSeriesNameUnderscore": "Naam_Serie",
-    "ValueEpisodeNamePeriod": "Naam.Aflevering",
-    "ValueEpisodeNameUnderscore": "Naam_Aflevering",
+    "LabelEpisodeName": "Aflevering naam",
+    "LabelSeriesName": "Serie naam",
+    "ValueSeriesNamePeriod": "Serie.Naam",
+    "ValueSeriesNameUnderscore": "Serie_naam",
+    "ValueEpisodeNamePeriod": "Aflevering.naam",
+    "ValueEpisodeNameUnderscore": "Aflevering_naam",
     "HeaderTypeText": "Voer tekst in",
-    "LabelTypeText": "tekst",
+    "LabelTypeText": "Tekst",
     "HeaderSearchForSubtitles": "Zoeken naar Ondertitels",
     "MessageNoSubtitleSearchResultsFound": "Geen zoekresultaten gevonden.",
     "TabDisplay": "Weergave",
@@ -800,7 +804,7 @@
     "LabelEnableChannelContentDownloadingFor": "Schakel kanaalinhoud downloaden in voor:",
     "LabelEnableChannelContentDownloadingForHelp": "Sommige kanalen ondersteunen downloaden en later kijken. Schakel deze optie in als er weinig bandbreedte beschikbaar is. Inhoud zal dan tijdens de kanaal download taak uitgevoerd worden.",
     "LabelChannelDownloadPath": "Kanaal inhoud download pad:",
-    "LabelChannelDownloadPathHelp": "Geef een eigen download pad op als dit gewenst is, leeglaten voor dowloaden naar de interne program data map.",
+    "LabelChannelDownloadPathHelp": "Geef een eigen download pad op als dit gewenst is, leeglaten voor dowloaden naar de interne programa data map.",
     "LabelChannelDownloadAge": "Verwijder inhoud na: (dagen)",
     "LabelChannelDownloadAgeHelp": "Gedownloade inhoud die ouder is zal worden verwijderd. Afspelen via internet streaming blijft mogelijk.",
     "ChannelSettingsFormHelp": "Installeer kanalen zoals Trailers en Vimeo in de Plug-in catalogus.",
@@ -836,7 +840,7 @@
     "LabelGroupChannelsIntoViews": "Toon de volgende kanalen binnen mijn overzichten:",
     "LabelGroupChannelsIntoViewsHelp": "Indien ingeschakeld, zullen deze kanalen direct naast andere overzichten worden weergegeven. Indien uitgeschakeld, zullen ze worden weergegeven in een aparte kanalen overzicht.",
     "LabelDisplayCollectionsView": "Toon Collecties in Mijn Overzichten om film collecties te tonen",
-    "LabelXbmcMetadataEnableExtraThumbs": "Kopieer extrafanart naar extrathunms",
+    "LabelXbmcMetadataEnableExtraThumbs": "Kopieer extrafanart naar extrathumbs",
     "LabelXbmcMetadataEnableExtraThumbsHelp": "Als er afbeeldingen gedownload worden kunnen deze in extrafanart en extrathumbs worden opgeslagen voor maximale compatibiliteit met XBMC skins",
     "TabServices": "Meta Diensten",
     "TabLogs": "Logboeken",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Hoofdstukken",
     "HeaderResumeSettings": "Instellingen voor Hervatten",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Gebruikers",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/pl.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 16 - 6
MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "Estes valores controlam como o Media Browser ser\u00e1 exibido no dispositivo.",
     "LabelMaxBitrate": "Taxa de bits m\u00e1xima:",
     "LabelMaxBitrateHelp": "Especifique uma taxa de bits m\u00e1xima para ambientes com restri\u00e7\u00e3o de tamanho de banda, ou se o dispositivo  imp\u00f5e esse limite.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignorar requisi\u00e7\u00f5es de extens\u00e3o do byte de transcodifica\u00e7\u00e3o",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "Se ativadas, estas requisi\u00e7\u00f5es ser\u00e3o honradas mas ir\u00e3o ignorar o cabe\u00e7alho da extens\u00e3o do byte.",
     "LabelFriendlyName": "Nome amig\u00e1vel",
@@ -767,10 +771,10 @@
     "OptionAuto": "Auto",
     "OptionYes": "Sim",
     "OptionNo": "N\u00e3o",
-    "LabelHomePageSection1": "Se\u00e7\u00e3o um da tela de in\u00edcio:",
-    "LabelHomePageSection2": "Se\u00e7\u00e3o dois da tela de in\u00edcio:",
-    "LabelHomePageSection3": "Se\u00e7\u00e3o tr\u00eas da tela de in\u00edcio:",
-    "LabelHomePageSection4": "Se\u00e7\u00e3o quatro da tela de in\u00edcio",
+    "LabelHomePageSection1": "Tela de in\u00edcio se\u00e7\u00e3o 1:",
+    "LabelHomePageSection2": "Tela de in\u00edcio se\u00e7\u00e3o 2:",
+    "LabelHomePageSection3": "Tela de in\u00edcio se\u00e7\u00e3o 3:",
+    "LabelHomePageSection4": "Tela de in\u00edcio se\u00e7\u00e3o 4:",
     "OptionMyViewsButtons": "Minhas visualiza\u00e7\u00f5es (bot\u00f5es)",
     "OptionMyViews": "Minhas visualiza\u00e7\u00f5es",
     "OptionMyViewsSmall": "Minhas visualiza\u00e7\u00f5es (pequeno)",
@@ -889,6 +893,12 @@
     "TabScheduledTasks": "Tarefas Agendadas",
     "HeaderChapters": "Cap\u00edtulos",
     "HeaderResumeSettings": "Ajustes para Retomar",
-    "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TabSync": "Sincroniza\u00e7\u00e3o",
+    "TitleUsers": "Usu\u00e1rios",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 27 - 17
MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json

@@ -190,7 +190,7 @@
     "HeaderStatus": "Estado",
     "OptionContinuing": "A Continuar",
     "OptionEnded": "Terminado",
-    "HeaderAirDays": "Dias de Exibi\u00e7\u00e3o:",
+    "HeaderAirDays": "Dias de Exibi\u00e7\u00e3o",
     "OptionSunday": "Domingo",
     "OptionMonday": "Segunda",
     "OptionTuesday": "Ter\u00e7a",
@@ -198,8 +198,8 @@
     "OptionThursday": "Quinta",
     "OptionFriday": "Sexta",
     "OptionSaturday": "S\u00e1bado",
-    "HeaderManagement": "Gest\u00e3o:",
-    "LabelManagement": "Management:",
+    "HeaderManagement": "Gest\u00e3o",
+    "LabelManagement": "Administra\u00e7\u00e3o:",
     "OptionMissingImdbId": "Id do IMDb em falta",
     "OptionMissingTvdbId": "iD do TheTVDB em falta",
     "OptionMissingOverview": "Descri\u00e7\u00e3o em falta",
@@ -257,11 +257,11 @@
     "ButtonSelectDirectory": "Selecione a diretoria",
     "LabelCustomPaths": "Defina localiza\u00e7\u00f5es personalizadas. Deixe os campos em branco para usar os valores padr\u00e3o.",
     "LabelCachePath": "Localiza\u00e7\u00e3o da cache:",
-    "LabelCachePathHelp": "Esta pasta cont\u00e9m ficheiros de cacha do servidor, como por exemplo, imagens.",
+    "LabelCachePathHelp": "Defina uma localiza\u00e7\u00e3o para ficheiros de cache do servidor, como por exemplo, imagens.",
     "LabelImagesByNamePath": "Localiza\u00e7\u00e3o das imagens por nome:",
-    "LabelImagesByNamePathHelp": "Esta pasta cont\u00e9m imagens de atores, artistas, g\u00e9neros e est\u00fadios.",
+    "LabelImagesByNamePathHelp": "Defina uma localiza\u00e7\u00e3o para imagens de atores, artistas, g\u00e9neros e est\u00fadios.",
     "LabelMetadataPath": "Localiza\u00e7\u00e3o dos metadados:",
-    "LabelMetadataPathHelp": "Esta localiza\u00e7\u00e3o cont\u00e9m imagens e metadados transferidos que n\u00e3o foram configurados para serem armazenados nas pastas multim\u00e9dia.",
+    "LabelMetadataPathHelp": "Defina uma localiza\u00e7\u00e3o para imagens e metadados transferidos que n\u00e3o foram configurados para serem armazenados nas pastas multim\u00e9dia.",
     "LabelTranscodingTempPath": "Localiza\u00e7\u00e3o tempor\u00e1ria das transcodifica\u00e7\u00f5es:",
     "LabelTranscodingTempPathHelp": "Esta pasta cont\u00e9m ficheiros de trabalho usados pelo transcodificador.",
     "TabBasics": "B\u00e1sico",
@@ -630,7 +630,7 @@
     "ButtonFullscreen": "Toggle fullscreen",
     "ButtonScenes": "Cenas",
     "ButtonSubtitles": "Legendas",
-    "ButtonAudioTracks": "Audio tracks",
+    "ButtonAudioTracks": "Faixas de \u00e1udio",
     "ButtonPreviousTrack": "Previous track",
     "ButtonNextTrack": "Next track",
     "ButtonStop": "Parar",
@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "Estes valores controlam como o Media Browser ser\u00e1 exibido no dispositivo.",
     "LabelMaxBitrate": "Taxa de bits m\u00e1xima:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Nome amig\u00e1vel",
@@ -871,24 +875,30 @@
     "HeaderNewApiKeyHelp": "Grant an application permission to communicate with Media Browser.",
     "HeaderHttpHeaders": "Http Headers",
     "HeaderIdentificationHeader": "Identification Header",
-    "LabelValue": "Value:",
+    "LabelValue": "Valor:",
     "LabelMatchType": "Match type:",
-    "OptionEquals": "Equals",
+    "OptionEquals": "Iguais",
     "OptionRegex": "Regex",
     "OptionSubstring": "Substring",
     "TabView": "View",
-    "TabSort": "Sort",
-    "TabFilter": "Filter",
-    "ButtonView": "View",
+    "TabSort": "Ordenar",
+    "TabFilter": "Filtro",
+    "ButtonView": "Visualizar",
     "LabelPageSize": "Item limit:",
-    "LabelView": "View:",
+    "LabelView": "Visualizar:",
     "TabUsers": "Users",
     "HeaderFeatures": "Features",
-    "HeaderAdvanced": "Advanced",
-    "ButtonSync": "Sync",
+    "HeaderAdvanced": "Avan\u00e7ado",
+    "ButtonSync": "Sincronizar",
     "TabScheduledTasks": "Scheduled Tasks",
-    "HeaderChapters": "Chapters",
+    "HeaderChapters": "Cap\u00edtulos",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 19 - 9
MediaBrowser.Server.Implementations/Localization/Server/ru.json

@@ -47,7 +47,7 @@
     "LabelSaveLocalMetadata": "\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0438 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 \u043c\u0435\u0434\u0438\u0430\u043f\u0430\u043f\u043e\u043a",
     "LabelSaveLocalMetadataHelp": "\u0418\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0438 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u043c\u0435\u0434\u0438\u0430\u043f\u0430\u043f\u043e\u043a, \u043f\u0440\u0438 \u0442\u0430\u043a\u043e\u043c \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u043b\u0435\u0433\u043a\u043e \u043f\u0440\u0430\u0432\u0438\u0442\u044c.",
     "LabelDownloadInternetMetadata": "\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0438 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430",
-    "LabelDownloadInternetMetadataHelp": "\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043d\u043e\u0441\u0438\u0442\u0435\u043b\u0435 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f Media Browser, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435 \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445.",
+    "LabelDownloadInternetMetadataHelp": "\u0414\u043b\u044f \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0441\u0432\u0435\u0434\u0435\u043d\u0438\u0439 \u043e \u043d\u043e\u0441\u0438\u0442\u0435\u043b\u0435 \u0432 Media Browser.",
     "TabPreferences": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438",
     "TabPassword": "\u041f\u0430\u0440\u043e\u043b\u044c",
     "TabLibraryAccess": "\u0414\u043e\u0441\u0442\u0443\u043f \u043a \u043c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0435",
@@ -337,7 +337,7 @@
     "OptionAutomatic": "\u0410\u0432\u0442\u043e",
     "LiveTvPluginRequired": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043b\u0430\u0433\u0438\u043d-\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a \u0443\u0441\u043b\u0443\u0433 \u044d\u0444\u0438\u0440\u043d\u043e\u0433\u043e \u0422\u0412 \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u044f.",
     "LiveTvPluginRequiredHelp": "\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u043e\u0434\u0438\u043d \u0438\u0437 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, NextPVR \u0438\u043b\u0438 ServerWMC.",
-    "LabelCustomizeOptionsPerMediaType": "\u041f\u043e\u0434\u0433\u043e\u043d\u043a\u0430 \u043f\u043e \u0442\u0438\u043f\u0443 \u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044f:",
+    "LabelCustomizeOptionsPerMediaType": "\u041f\u043e\u0434\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u043e \u0442\u0438\u043f\u0443 \u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044f:",
     "OptionDownloadThumbImage": "\u0411\u0435\u0433\u0443\u043d\u043e\u043a",
     "OptionDownloadMenuImage": "\u041c\u0435\u043d\u044e",
     "OptionDownloadLogoImage": "\u041b\u043e\u0433\u043e\u0442\u0438\u043f",
@@ -590,7 +590,7 @@
     "NotificationOptionInstallationFailed": "\u0421\u0431\u043e\u0439 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438",
     "NotificationOptionNewLibraryContent": "\u041d\u043e\u0432\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e",
     "NotificationOptionNewLibraryContentMultiple": "\u041d\u043e\u0432\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e (\u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e)",
-    "SendNotificationHelp": "\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432 \u044f\u0449\u0438\u043a \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u0418\u043d\u0444\u043e\u043f\u0430\u043d\u0435\u043b\u0438. \u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439.",
+    "SendNotificationHelp": "\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432 \u044f\u0449\u0438\u043a \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u0432 \u0418\u043d\u0444\u043e\u043f\u0430\u043d\u0435\u043b\u0438. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439.",
     "NotificationOptionServerRestartRequired": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0430",
     "LabelNotificationEnabled": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u043e\u0435 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0435",
     "LabelMonitorUsers": "\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043e\u0442:",
@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "\u042d\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442, \u043a\u0430\u043a Media Browser \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435.",
     "LabelMaxBitrate": "\u041c\u0430\u043a\u0441. \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u0430\u044f \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c:",
     "LabelMaxBitrateHelp": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u0443\u044e \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0432 \u0441\u0440\u0435\u0434\u0430\u0445 \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u043e\u0439 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u043d\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c\u044e, \u043b\u0438\u0431\u043e \u0435\u0441\u043b\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u0441\u0432\u043e\u0451 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0430 \u0431\u0430\u0439\u0442\u043e\u0432 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0438",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "\u041f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438, \u044d\u0442\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0431\u0443\u0434\u0443\u0442 \u0443\u0447\u0442\u0435\u043d\u044b, \u043d\u043e \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0430 \u0431\u0430\u0439\u0442\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u043d.",
     "LabelFriendlyName": "\u041f\u043e\u043d\u044f\u0442\u043d\u043e\u0435 \u0438\u043c\u044f",
@@ -705,7 +709,7 @@
     "HeaderIdentificationCriteriaHelp": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435, \u043f\u043e \u043a\u0440\u0430\u0439\u043d\u0435\u0439 \u043c\u0435\u0440\u0435, \u043e\u0434\u0438\u043d \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u0439.",
     "HeaderDirectPlayProfileHelp": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043f\u0440\u043e\u0444\u0438\u043b\u0438 \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u0443\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u043c \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e.",
     "HeaderTranscodingProfileHelp": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043f\u0440\u043e\u0444\u0438\u043b\u0438 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u0443\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430.",
-    "HeaderResponseProfileHelp": "\u041f\u0440\u043e\u0444\u0438\u043b\u0438 \u043e\u0442\u043a\u043b\u0438\u043a\u043e\u0432 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u0434\u043e\u0433\u043d\u0430\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u043f\u043e\u0441\u044b\u043b\u0430\u0435\u043c\u0443\u044e \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043f\u0440\u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0432\u0438\u0434\u043e\u0432 \u043d\u043e\u0441\u0438\u0442\u0435\u043b\u0435\u0439.",
+    "HeaderResponseProfileHelp": "\u041f\u0440\u043e\u0444\u0438\u043b\u0438 \u043e\u0442\u043a\u043b\u0438\u043a\u043e\u0432 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u0441\u043f\u043e\u0441\u043e\u0431 \u0434\u043b\u044f \u043f\u043e\u0434\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u043f\u043e\u0441\u044b\u043b\u0430\u0435\u043c\u043e\u0439 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043f\u0440\u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0432\u0438\u0434\u043e\u0432 \u043d\u043e\u0441\u0438\u0442\u0435\u043b\u0435\u0439.",
     "LabelXDlnaCap": "\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u0430 X-Dlna:",
     "LabelXDlnaCapHelp": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 X_DLNACAP \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 \u0438\u043c\u0451\u043d urn:schemas-dlna-org:device-1-0",
     "LabelXDlnaDoc": "\u0421\u0445\u0435\u043c\u0430 X-Dlna:",
@@ -792,7 +796,7 @@
     "MessageNoCollectionsAvailable": "\u041a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u043c \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0438\u0437 \u0444\u0438\u043b\u044c\u043c\u043e\u0432, \u0441\u0435\u0440\u0438\u0430\u043b\u043e\u0432, \u0430\u043b\u044c\u0431\u043e\u043c\u043e\u0432 \u0438 \u0438\u0433\u0440. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \"\u0421\u043e\u0437\u0434\u0430\u0442\u044c\", \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044e.",
     "HeaderWelcomeToMediaBrowserWebClient": "\u0412\u0435\u0431-\u043a\u043b\u0438\u0435\u043d\u0442 Media Browser \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0432\u0430\u0441!",
     "ButtonDismiss": "\u0421\u043a\u0440\u044b\u0442\u044c",
-    "MessageLearnHowToCustomize": "\u0418\u0437\u0443\u0447\u0438\u0442\u0435, \u043a\u0430\u043a \u043f\u043e\u0434\u043e\u0433\u043d\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043f\u043e \u0441\u0432\u043e\u0435\u043c\u0443 \u0432\u043a\u0443\u0441\u0443. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u0430\u0432\u0430\u0442\u0430\u0440\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u043f\u0440\u0430\u0432\u043e\u043c \u0432\u0435\u0440\u0445\u043d\u0435\u043c \u0443\u0433\u043b\u0443 \u044d\u043a\u0440\u0430\u043d\u0430, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0438 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0432\u043e\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438.",
+    "MessageLearnHowToCustomize": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c, \u043a\u0430\u043a \u043f\u043e\u0434\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043f\u043e \u0441\u0432\u043e\u0435\u043c\u0443 \u043b\u0438\u0447\u043d\u043e\u043c\u0443 \u0432\u043a\u0443\u0441\u0443. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u0430\u0432\u0430\u0442\u0430\u0440\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u043f\u0440\u0430\u0432\u043e\u043c \u0432\u0435\u0440\u0445\u043d\u0435\u043c \u0443\u0433\u043b\u0443 \u044d\u043a\u0440\u0430\u043d\u0430, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0438 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0432\u043e\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438.",
     "ButtonEditOtherUserPreferences": "\u041f\u0440\u0430\u0432\u0438\u0442\u044c \u043b\u0438\u0447\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.",
     "LabelChannelStreamQuality": "\u041a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u043e\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442:",
     "LabelChannelStreamQualityHelp": "\u0412 \u0441\u0440\u0435\u0434\u0435 \u0441 \u043d\u0438\u0437\u043a\u043e\u0439 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u043d\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c\u044e, \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043c\u043e\u0447\u044c \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0432\u043f\u0435\u0447\u0430\u0442\u043b\u0435\u043d\u0438\u0435 \u0431\u043e\u043b\u0435\u0435 \u043f\u043b\u0430\u0432\u043d\u043e\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u043e\u0433\u043e \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f.",
@@ -842,7 +846,7 @@
     "TabLogs": "\u0416\u0443\u0440\u043d\u0430\u043b\u044b",
     "HeaderServerLogFiles": "\u0424\u0430\u0439\u043b\u044b \u0436\u0443\u0440\u043d\u0430\u043b\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430:",
     "TabBranding": "\u0411\u0440\u0435\u043d\u0434\u0438\u043d\u0433",
-    "HeaderBrandingHelp": "\u041f\u043e\u0434\u0433\u043e\u043d\u043a\u0430 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0432\u0438\u0434\u0430 Media Browser \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u044f\u043c\u0438 \u0432\u0430\u0448\u0435\u0439 \u0433\u0440\u0443\u043f\u043f\u044b \u0438\u043b\u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438.",
+    "HeaderBrandingHelp": "\u041f\u043e\u0434\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u0432\u0438\u0434 Media Browser \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u044f\u043c\u0438 \u0432\u0430\u0448\u0435\u0439 \u0433\u0440\u0443\u043f\u043f\u044b \u0438\u043b\u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438.",
     "LabelLoginDisclaimer": "\u041e\u0433\u043e\u0432\u043e\u0440\u043a\u0430 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0432\u0445\u043e\u0434\u0430:",
     "LabelLoginDisclaimerHelp": "\u042d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u0432 \u043d\u0438\u0436\u043d\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443.",
     "LabelAutomaticallyDonate": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0436\u0435\u0440\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0441\u0443\u043c\u043c\u0443 \u043a\u0430\u0436\u0434\u044b\u0435 \u0448\u0435\u0441\u0442\u044c \u043c\u0435\u0441\u044f\u0446\u0435\u0432",
@@ -858,7 +862,7 @@
     "HeaderLatestMusic": "\u041d\u043e\u0432\u0438\u043d\u043a\u0438 \u043c\u0443\u0437\u044b\u043a\u0438",
     "HeaderBranding": "\u0411\u0440\u0435\u043d\u0434\u0438\u043d\u0433",
     "HeaderApiKeys": "\u041a\u043b\u044e\u0447\u0438 API",
-    "HeaderApiKeysHelp": "\u0412\u043d\u0435\u0448\u043d\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043a\u043b\u044e\u0447 API, \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a Media Browser. \u041a\u043b\u044e\u0447\u0438 \u0432\u044b\u0434\u0430\u044e\u0442\u0441\u044f \u043f\u0440\u0438 \u0432\u0445\u043e\u0434\u0435 \u0441 \u0443\u0447\u0451\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u044c\u044e Media Browser, \u0438\u043b\u0438 \u043a\u043b\u044e\u0447 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0440\u0443\u0447\u043d\u0443\u044e.",
+    "HeaderApiKeysHelp": "\u0412\u043d\u0435\u0448\u043d\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043a\u043b\u044e\u0447 API, \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a Media Browser. \u041a\u043b\u044e\u0447\u0438 \u0432\u044b\u0434\u0430\u044e\u0442\u0441\u044f \u043f\u0440\u0438 \u0432\u0445\u043e\u0434\u0435 \u0441 \u0443\u0447\u0451\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u044c\u044e Media Browser, \u0438\u043b\u0438 \u043a\u043b\u044e\u0447 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u0432\u0440\u0443\u0447\u043d\u0443\u044e.",
     "HeaderApiKey": "\u041a\u043b\u044e\u0447 API",
     "HeaderApp": "\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435",
     "HeaderDevice": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e",
@@ -889,6 +893,12 @@
     "TabScheduledTasks": "\u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a",
     "HeaderChapters": "\u0421\u0446\u0435\u043d\u044b",
     "HeaderResumeSettings": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f",
-    "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TabSync": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f",
+    "TitleUsers": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/sv.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "Dessa v\u00e4rden styr hur Media Browser presenterar sig f\u00f6r enheten.",
     "LabelMaxBitrate": "H\u00f6gsta bithastighet:",
     "LabelMaxBitrateHelp": "Ange en h\u00f6gsta bithastighet i bandbreddsbegr\u00e4nsade milj\u00f6er, eller i fall d\u00e4r enheten har sina egna begr\u00e4nsningar.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignorera beg\u00e4ran om \"byte range\" vid omkodning",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "Om aktiverad kommer beg\u00e4ran att uppfyllas, men \"byte range\"-rubriken ignoreras.",
     "LabelFriendlyName": "\u00d6nskat namn",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/vi.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 11 - 1
MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json

@@ -691,6 +691,10 @@
     "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.",
     "LabelMaxBitrate": "Max bitrate:",
     "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.",
+    "LabelMaxStreamingBitrate": "Max streaming bitrate:",
+    "LabelMaxStreamingBitrateHelp": "Specify a max bitrate when streaming.",
+    "LabelMaxStaticBitrate": "Max sync bitrate:",
+    "LabelMaxStaticBitrateHelp": "Specify a max bitrate when syncing content at high quality.",
     "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
     "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.",
     "LabelFriendlyName": "Friendly name",
@@ -890,5 +894,11 @@
     "HeaderChapters": "Chapters",
     "HeaderResumeSettings": "Resume Settings",
     "TabSync": "Sync",
-    "TitleUsers": "Users"
+    "TitleUsers": "Users",
+    "LabelProtocol": "Protocol:",
+    "OptionProtocolHttp": "Http",
+    "OptionProtocolHls": "Http Live Streaming",
+    "LabelContext": "Context:",
+    "OptionContextStreaming": "Streaming",
+    "OptionContextStatic": "Sync"
 }

+ 0 - 9
MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs

@@ -61,15 +61,6 @@ namespace MediaBrowser.Server.Implementations.ServerManager
         /// <value>The configuration manager.</value>
         private IServerConfigurationManager ConfigurationManager { get; set; }
 
-        /// <summary>
-        /// Gets the web socket port number.
-        /// </summary>
-        /// <value>The web socket port number.</value>
-        public int WebSocketPortNumber
-        {
-            get { return ConfigurationManager.Configuration.HttpServerPortNumber; }
-        }
-
         /// <summary>
         /// Gets the web socket listeners.
         /// </summary>

+ 25 - 0
MediaBrowser.Server.Implementations/Session/WebSocketController.cs

@@ -234,6 +234,8 @@ namespace MediaBrowser.Server.Implementations.Session
 
         private Task SendMessage<T>(WebSocketMessage<T> message, CancellationToken cancellationToken)
         {
+            if (SkipSending()) return Task.FromResult(true);
+
             var socket = GetActiveSocket();
 
             return socket.SendAsync(message, cancellationToken);
@@ -241,6 +243,8 @@ namespace MediaBrowser.Server.Implementations.Session
 
         private Task SendMessages<T>(WebSocketMessage<T> message, CancellationToken cancellationToken)
         {
+            if (SkipSending()) return Task.FromResult(true);
+
             var tasks = GetActiveSockets().Select(i => Task.Run(async () =>
             {
                 try
@@ -257,6 +261,27 @@ namespace MediaBrowser.Server.Implementations.Session
             return Task.WhenAll(tasks);
         }
 
+        private bool SkipSending()
+        {
+            if (Session != null)
+            {
+                if (string.Equals(Session.Client, "mb-classic", StringComparison.OrdinalIgnoreCase))
+                {
+                    Version version;
+
+                    if (!string.IsNullOrWhiteSpace(Session.ApplicationVersion) && Version.TryParse(Session.ApplicationVersion, out version))
+                    {
+                        if (version < new Version(3, 0, 196))
+                        {
+                            _logger.Debug("Skipping web socket message to MBC version {0}.", version);
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+
         public void Dispose()
         {
             foreach (var socket in Sockets.ToList())

+ 11 - 3
MediaBrowser.Server.Implementations/Udp/UdpServer.cs

@@ -54,13 +54,14 @@ namespace MediaBrowser.Server.Implementations.Udp
         /// <param name="serverConfigurationManager">The server configuration manager.</param>
         /// <param name="httpServer">The HTTP server.</param>
         /// <param name="appHost">The application host.</param>
-        public UdpServer(ILogger logger, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager, IHttpServer httpServer, IServerApplicationHost appHost)
+        public UdpServer(ILogger logger, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager, IHttpServer httpServer, IServerApplicationHost appHost, IJsonSerializer json)
         {
             _logger = logger;
             _networkManager = networkManager;
             _serverConfigurationManager = serverConfigurationManager;
             _httpServer = httpServer;
             _appHost = appHost;
+            _json = json;
 
             AddMessageResponder("who is MediaBrowserServer?", RespondToV1Message);
             AddMessageResponder("who is MediaBrowserServer_v2?", RespondToV2Message);
@@ -77,13 +78,20 @@ namespace MediaBrowser.Server.Implementations.Udp
         /// Raises the <see cref="E:MessageReceived" /> event.
         /// </summary>
         /// <param name="e">The <see cref="UdpMessageReceivedEventArgs"/> instance containing the event data.</param>
-        private async void OnMessageReceived(UdpMessageReceivedEventArgs e)
+        private void OnMessageReceived(UdpMessageReceivedEventArgs e)
         {
             var responder = _responders.FirstOrDefault(i => i.Item1.SequenceEqual(e.Bytes));
 
             if (responder != null)
             {
-                responder.Item2(e.RemoteEndPoint);
+                try
+                {
+                    responder.Item2(e.RemoteEndPoint);
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error in OnMessageReceived", ex);
+                }
             }
         }
 

+ 1 - 1
MediaBrowser.Server.Mono/Native/ServerAuthorization.cs

@@ -18,7 +18,7 @@ namespace MediaBrowser.ServerApplication.Native
         /// <param name="webSocketPort">The web socket port.</param>
         /// <param name="udpPort">The UDP port.</param>
         /// <param name="tempDirectory">The temp directory.</param>
-        public static void AuthorizeServer(int httpServerPort, string httpServerUrlPrefix, int webSocketPort, int udpPort, string tempDirectory)
+        public static void AuthorizeServer(int httpServerPort, string httpServerUrlPrefix, int udpPort, string tempDirectory)
         {
 
         }

+ 7 - 7
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -533,8 +533,8 @@ namespace MediaBrowser.ServerApplication
             AuthenticationRepository = await GetAuthenticationRepository().ConfigureAwait(false);
             RegisterSingleInstance(AuthenticationRepository);
 
-            SyncRepository = await GetSyncRepository().ConfigureAwait(false);
-            RegisterSingleInstance(SyncRepository);
+            //SyncRepository = await GetSyncRepository().ConfigureAwait(false);
+            //RegisterSingleInstance(SyncRepository);
 
             UserManager = new UserManager(LogManager.GetLogger("UserManager"), ServerConfigurationManager, UserRepository, XmlSerializer);
             RegisterSingleInstance(UserManager);
@@ -598,9 +598,6 @@ namespace MediaBrowser.ServerApplication
             var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("Dlna"), JsonSerializer);
             RegisterSingleInstance<IDlnaManager>(dlnaManager);
 
-            var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient);
-            RegisterSingleInstance<IContentDirectory>(contentDirectory);
-
             var connectionManager = new ConnectionManager(dlnaManager, ServerConfigurationManager, LogManager.GetLogger("UpnpConnectionManager"), HttpClient);
             RegisterSingleInstance<IConnectionManager>(connectionManager);
 
@@ -610,9 +607,12 @@ namespace MediaBrowser.ServerApplication
             LiveTvManager = new LiveTvManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager);
             RegisterSingleInstance(LiveTvManager);
 
-            UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, FileSystemManager, UserManager, ChannelManager, LiveTvManager);
+            UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, FileSystemManager, UserManager, ChannelManager, LiveTvManager, ApplicationPaths);
             RegisterSingleInstance(UserViewManager);
 
+            var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, UserViewManager, ChannelManager);
+            RegisterSingleInstance<IContentDirectory>(contentDirectory);
+
             NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager);
             RegisterSingleInstance(NotificationManager);
 
@@ -1024,7 +1024,7 @@ namespace MediaBrowser.ServerApplication
                 HasPendingRestart = HasPendingRestart,
                 Version = ApplicationVersion.ToString(),
                 IsNetworkDeployed = CanSelfUpdate,
-                WebSocketPortNumber = ServerManager.WebSocketPortNumber,
+                WebSocketPortNumber = HttpServerPort,
                 SupportsNativeWebSocket = true,
                 FailedPluginAssemblies = FailedAssemblies.ToList(),
                 InProgressInstallations = InstallationManager.CurrentInstallations.Select(i => i.Item1).ToList(),

+ 4 - 4
MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs

@@ -35,7 +35,7 @@ namespace MediaBrowser.ServerApplication.FFMpeg
                     switch (arg)
                     {
                         case "Version":
-                            return "20140721";
+                            return "20140612";
                         case "FFMpegFilename":
                             return "ffmpeg.exe";
                         case "FFProbeFilename":
@@ -113,8 +113,8 @@ namespace MediaBrowser.ServerApplication.FFMpeg
                 case PlatformID.Win32NT:
                     return new[]
                     {
-                        "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20140721-git-ce385c8-win32-static.7z",
-                        "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/windows/ffmpeg-20140721-git-ce385c8-win32-static.7z"
+                        "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20140612-git-3a1c895-win32-static.7z",
+                        "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/windows/ffmpeg-20140612-git-3a1c895-win32-static.7z"
                     };
 
                 case PlatformID.Unix:
@@ -222,4 +222,4 @@ namespace MediaBrowser.ServerApplication.FFMpeg
         public string sysname = string.Empty;
         public string machine = string.Empty;
     }
-}
+}

+ 1 - 7
MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs

@@ -1,12 +1,10 @@
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Progress;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Net;
 using System;
-using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Text;
@@ -140,13 +138,9 @@ namespace MediaBrowser.ServerApplication.FFMpeg
                     ExtractFFMpeg(tempFile, directory);
                     return;
                 }
-                catch (HttpException ex)
-                {
-                    _logger.ErrorException("Error downloading {0}", ex, url);
-                }
                 catch (Exception ex)
                 {
-                    _logger.ErrorException("Error unpacking {0}", ex, url);
+                    _logger.ErrorException("Error downloading {0}", ex, url);
                 }
             }
 

+ 2 - 0
MediaBrowser.Tests/Resolvers/TvUtilTests.cs

@@ -166,6 +166,8 @@ namespace MediaBrowser.Tests.Resolvers
             Assert.AreEqual(1, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 1"));
             Assert.AreEqual(1, TVUtils.GetSeasonNumberFromPath(@"\Drive\Season 1"));
 
+            Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Seinfeld\S02"));
+            
             Assert.AreEqual(2, TVUtils.GetSeasonNumberFromPath(@"\Drive\Seinfeld\2"));
             
             //Four Digits seasons

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