浏览代码

re-enable chromecast

Luke Pulverenti 10 年之前
父节点
当前提交
cd1f097167
共有 49 个文件被更改,包括 541 次插入620 次删除
  1. 13 12
      MediaBrowser.Api/ChannelService.cs
  2. 2 2
      MediaBrowser.Api/ConfigurationService.cs
  3. 4 4
      MediaBrowser.Api/Dlna/DlnaServerService.cs
  4. 5 19
      MediaBrowser.Api/Images/RemoteImageService.cs
  5. 39 39
      MediaBrowser.Api/LiveTv/LiveTvService.cs
  6. 3 4
      MediaBrowser.Api/Movies/CollectionService.cs
  7. 3 7
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  8. 33 13
      MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
  9. 1 1
      MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
  10. 12 0
      MediaBrowser.Api/Playback/Progressive/AudioService.cs
  11. 35 22
      MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
  12. 4 3
      MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
  13. 17 2
      MediaBrowser.Api/Playback/Progressive/VideoService.cs
  14. 3 4
      MediaBrowser.Api/PlaylistService.cs
  15. 2 2
      MediaBrowser.Api/SearchService.cs
  16. 4 6
      MediaBrowser.Api/Sync/SyncService.cs
  17. 14 27
      MediaBrowser.Api/UserLibrary/PlaystateService.cs
  18. 2 2
      MediaBrowser.Api/UserLibrary/UserLibraryService.cs
  19. 13 25
      MediaBrowser.Api/UserService.cs
  20. 6 20
      MediaBrowser.Api/VideosService.cs
  21. 9 9
      MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
  22. 34 2
      MediaBrowser.Dlna/Didl/DidlBuilder.cs
  23. 0 3
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  24. 26 16
      MediaBrowser.Model/Dlna/SearchCriteria.cs
  25. 2 1
      MediaBrowser.Model/Dlna/SearchType.cs
  26. 0 12
      MediaBrowser.Model/Dto/BaseItemDto.cs
  27. 24 0
      MediaBrowser.Model/Extensions/StringHelper.cs
  28. 0 8
      MediaBrowser.Model/Logging/ILogger.cs
  29. 3 5
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  30. 44 44
      MediaBrowser.Server.Implementations/Localization/JavaScript/es.json
  31. 22 22
      MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json
  32. 6 6
      MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json
  33. 1 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
  34. 23 23
      MediaBrowser.Server.Implementations/Localization/JavaScript/kk.json
  35. 20 20
      MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json
  36. 26 26
      MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json
  37. 23 23
      MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json
  38. 4 4
      MediaBrowser.Server.Implementations/Localization/JavaScript/sv.json
  39. 5 5
      MediaBrowser.Server.Implementations/Localization/Server/es.json
  40. 1 1
      MediaBrowser.Server.Implementations/Localization/Server/es_MX.json
  41. 3 3
      MediaBrowser.Server.Implementations/Localization/Server/fr.json
  42. 4 4
      MediaBrowser.Server.Implementations/Localization/Server/kk.json
  43. 13 13
      MediaBrowser.Server.Implementations/Localization/Server/nl.json
  44. 11 11
      MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json
  45. 2 2
      MediaBrowser.Server.Implementations/Localization/Server/ru.json
  46. 1 1
      MediaBrowser.Server.Implementations/Localization/Server/server.json
  47. 15 15
      MediaBrowser.Server.Implementations/Localization/Server/sv.json
  48. 2 126
      MediaBrowser.ServerApplication/ApplicationHost.cs
  49. 2 0
      MediaBrowser.WebDashboard/Api/DashboardService.cs

+ 13 - 12
MediaBrowser.Api/ChannelService.cs

@@ -9,6 +9,7 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading;
+using System.Threading.Tasks;
 
 namespace MediaBrowser.Api
 {
@@ -198,14 +199,14 @@ namespace MediaBrowser.Api
             return ToOptimizedResult(result);
         }
 
-        public object Get(GetChannelFolder request)
+        public async Task<object> Get(GetChannelFolder request)
         {
-            return ToOptimizedResult(_channelManager.GetChannelFolder(request.UserId, CancellationToken.None).Result);
+            return ToOptimizedResult(await _channelManager.GetChannelFolder(request.UserId, CancellationToken.None).ConfigureAwait(false));
         }
-        
-        public object Get(GetChannels request)
+
+        public async Task<object> Get(GetChannels request)
         {
-            var result = _channelManager.GetChannels(new ChannelQuery
+            var result = await _channelManager.GetChannels(new ChannelQuery
             {
                 Limit = request.Limit,
                 StartIndex = request.StartIndex,
@@ -213,14 +214,14 @@ namespace MediaBrowser.Api
                 SupportsLatestItems = request.SupportsLatestItems,
                 IsFavorite = request.IsFavorite
 
-            }, CancellationToken.None).Result;
+            }, CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedResult(result);
         }
 
-        public object Get(GetChannelItems request)
+        public async Task<object> Get(GetChannelItems request)
         {
-            var result = _channelManager.GetChannelItems(new ChannelItemQuery
+            var result = await _channelManager.GetChannelItems(new ChannelItemQuery
             {
                 Limit = request.Limit,
                 StartIndex = request.StartIndex,
@@ -232,14 +233,14 @@ namespace MediaBrowser.Api
                 Filters = request.GetFilters().ToArray(),
                 Fields = request.GetItemFields().ToArray()
 
-            }, CancellationToken.None).Result;
+            }, CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedResult(result);
         }
 
-        public object Get(GetLatestChannelItems request)
+        public async Task<object> Get(GetLatestChannelItems request)
         {
-            var result = _channelManager.GetLatestChannelItems(new AllChannelMediaQuery
+            var result = await _channelManager.GetLatestChannelItems(new AllChannelMediaQuery
             {
                 Limit = request.Limit,
                 StartIndex = request.StartIndex,
@@ -248,7 +249,7 @@ namespace MediaBrowser.Api
                 Filters = request.GetFilters().ToArray(),
                 Fields = request.GetItemFields().ToList()
 
-            }, CancellationToken.None).Result;
+            }, CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedResult(result);
         }

+ 2 - 2
MediaBrowser.Api/ConfigurationService.cs

@@ -1,5 +1,4 @@
-using System;
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Library;
@@ -10,6 +9,7 @@ using MediaBrowser.Model.Serialization;
 using ServiceStack;
 using ServiceStack.Text.Controller;
 using ServiceStack.Web;
+using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;

+ 4 - 4
MediaBrowser.Api/Dlna/DlnaServerService.cs

@@ -109,16 +109,16 @@ namespace MediaBrowser.Api.Dlna
             return ResultFactory.GetResult(xml, "text/xml");
         }
 
-        public object Post(ProcessContentDirectoryControlRequest request)
+        public async Task<object> Post(ProcessContentDirectoryControlRequest request)
         {
-            var response = PostAsync(request.RequestStream, _contentDirectory).Result;
+            var response = await PostAsync(request.RequestStream, _contentDirectory).ConfigureAwait(false);
 
             return ResultFactory.GetResult(response.Xml, "text/xml");
         }
 
-        public object Post(ProcessConnectionManagerControlRequest request)
+        public async Task<object> Post(ProcessConnectionManagerControlRequest request)
         {
-            var response = PostAsync(request.RequestStream, _connectionManager).Result;
+            var response = await PostAsync(request.RequestStream, _connectionManager).ConfigureAwait(false);
 
             return ResultFactory.GetResult(response.Xml, "text/xml");
         }

+ 5 - 19
MediaBrowser.Api/Images/RemoteImageService.cs

@@ -203,9 +203,7 @@ namespace MediaBrowser.Api.Images
         {
             var item = _libraryManager.GetItemById(request.Id);
 
-            var result = GetRemoteImageResult(item, request);
-
-            return ToOptimizedSerializedResultUsingCache(result);
+            return GetRemoteImageResult(item, request);
         }
 
         public object Get(GetItemByNameRemoteImages request)
@@ -218,16 +216,16 @@ namespace MediaBrowser.Api.Images
             return GetRemoteImageResult(item, request);
         }
 
-        private RemoteImageResult GetRemoteImageResult(BaseItem item, BaseRemoteImageRequest request)
+        private async Task<RemoteImageResult> GetRemoteImageResult(BaseItem item, BaseRemoteImageRequest request)
         {
-            var images = _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery
+            var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery
             {
                 ProviderName = request.ProviderName,
                 IncludeAllLanguages = request.IncludeAllLanguages,
                 IncludeDisabledProviders = true,
                 ImageType = request.Type
 
-            }, CancellationToken.None).Result;
+            }, CancellationToken.None).ConfigureAwait(false);
 
             var imagesList = images.ToList();
 
@@ -306,19 +304,7 @@ namespace MediaBrowser.Api.Images
         /// </summary>
         /// <param name="request">The request.</param>
         /// <returns>System.Object.</returns>
-        public object Get(GetRemoteImage request)
-        {
-            var task = GetRemoteImage(request);
-
-            return task.Result;
-        }
-
-        /// <summary>
-        /// Gets the remote image.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>Task{System.Object}.</returns>
-        private async Task<object> GetRemoteImage(GetRemoteImage request)
+        public async Task<object> Get(GetRemoteImage request)
         {
             var urlHash = request.ImageUrl.GetMD5();
             var pointerCachePath = GetFullCachePath(urlHash.ToString());

+ 39 - 39
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -295,16 +295,16 @@ namespace MediaBrowser.Api.LiveTv
             }
         }
 
-        public object Get(GetLiveTvInfo request)
+        public async Task<object> Get(GetLiveTvInfo request)
         {
-            var info = _liveTvManager.GetLiveTvInfo(CancellationToken.None).Result;
+            var info = await _liveTvManager.GetLiveTvInfo(CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(info);
         }
 
-        public object Get(GetChannels request)
+        public async Task<object> Get(GetChannels request)
         {
-            var result = _liveTvManager.GetChannels(new LiveTvChannelQuery
+            var result = await _liveTvManager.GetChannels(new LiveTvChannelQuery
             {
                 ChannelType = request.Type,
                 UserId = request.UserId,
@@ -314,26 +314,26 @@ namespace MediaBrowser.Api.LiveTv
                 IsLiked = request.IsLiked,
                 IsDisliked = request.IsDisliked
 
-            }, CancellationToken.None).Result;
+            }, CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
 
-        public object Get(GetChannel request)
+        public async Task<object> Get(GetChannel request)
         {
             var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(new Guid(request.UserId));
 
-            var result = _liveTvManager.GetChannel(request.Id, CancellationToken.None, user).Result;
+            var result = await _liveTvManager.GetChannel(request.Id, CancellationToken.None, user).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
 
-        public object Get(GetLiveTvFolder request)
+        public async Task<object> Get(GetLiveTvFolder request)
         {
-            return ToOptimizedResult(_liveTvManager.GetLiveTvFolder(request.UserId, CancellationToken.None).Result);
+            return ToOptimizedResult(await _liveTvManager.GetLiveTvFolder(request.UserId, CancellationToken.None).ConfigureAwait(false));
         }
 
-        public object Get(GetPrograms request)
+        public async Task<object> Get(GetPrograms request)
         {
             var query = new ProgramQuery
             {
@@ -361,12 +361,12 @@ namespace MediaBrowser.Api.LiveTv
                 query.MaxEndDate = DateTime.Parse(request.MaxEndDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
             }
 
-            var result = _liveTvManager.GetPrograms(query, CancellationToken.None).Result;
+            var result = await _liveTvManager.GetPrograms(query, CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
 
-        public object Get(GetRecommendedPrograms request)
+        public async Task<object> Get(GetRecommendedPrograms request)
         {
             var query = new RecommendedProgramQuery
             {
@@ -376,7 +376,7 @@ namespace MediaBrowser.Api.LiveTv
                 HasAired = request.HasAired
             };
 
-            var result = _liveTvManager.GetRecommendedPrograms(query, CancellationToken.None).Result;
+            var result = await _liveTvManager.GetRecommendedPrograms(query, CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
@@ -386,9 +386,9 @@ namespace MediaBrowser.Api.LiveTv
             return Get(request);
         }
 
-        public object Get(GetRecordings request)
+        public async Task<object> Get(GetRecordings request)
         {
-            var result = _liveTvManager.GetRecordings(new RecordingQuery
+            var result = await _liveTvManager.GetRecordings(new RecordingQuery
             {
                 ChannelId = request.ChannelId,
                 UserId = request.UserId,
@@ -399,35 +399,35 @@ namespace MediaBrowser.Api.LiveTv
                 SeriesTimerId = request.SeriesTimerId,
                 IsInProgress = request.IsInProgress
 
-            }, CancellationToken.None).Result;
+            }, CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
 
-        public object Get(GetRecording request)
+        public async Task<object> Get(GetRecording request)
         {
             var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(new Guid(request.UserId));
 
-            var result = _liveTvManager.GetRecording(request.Id, CancellationToken.None, user).Result;
+            var result = await _liveTvManager.GetRecording(request.Id, CancellationToken.None, user).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
 
-        public object Get(GetTimer request)
+        public async Task<object> Get(GetTimer request)
         {
-            var result = _liveTvManager.GetTimer(request.Id, CancellationToken.None).Result;
+            var result = await _liveTvManager.GetTimer(request.Id, CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
 
-        public object Get(GetTimers request)
+        public async Task<object> Get(GetTimers request)
         {
-            var result = _liveTvManager.GetTimers(new TimerQuery
+            var result = await _liveTvManager.GetTimers(new TimerQuery
             {
                 ChannelId = request.ChannelId,
                 SeriesTimerId = request.SeriesTimerId
 
-            }, CancellationToken.None).Result;
+            }, CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
@@ -459,21 +459,21 @@ namespace MediaBrowser.Api.LiveTv
             Task.WaitAll(task);
         }
 
-        public object Get(GetSeriesTimers request)
+        public async Task<object> Get(GetSeriesTimers request)
         {
-            var result = _liveTvManager.GetSeriesTimers(new SeriesTimerQuery
+            var result = await _liveTvManager.GetSeriesTimers(new SeriesTimerQuery
             {
                 SortOrder = request.SortOrder,
                 SortBy = request.SortBy
 
-            }, CancellationToken.None).Result;
+            }, CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
 
-        public object Get(GetSeriesTimer request)
+        public async Task<object> Get(GetSeriesTimer request)
         {
-            var result = _liveTvManager.GetSeriesTimer(request.Id, CancellationToken.None).Result;
+            var result = await _liveTvManager.GetSeriesTimer(request.Id, CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
@@ -496,27 +496,27 @@ namespace MediaBrowser.Api.LiveTv
             Task.WaitAll(task);
         }
 
-        public object Get(GetDefaultTimer request)
+        public async Task<object> Get(GetDefaultTimer request)
         {
             if (string.IsNullOrEmpty(request.ProgramId))
             {
-                var result = _liveTvManager.GetNewTimerDefaults(CancellationToken.None).Result;
+                var result = await _liveTvManager.GetNewTimerDefaults(CancellationToken.None).ConfigureAwait(false);
 
                 return ToOptimizedSerializedResultUsingCache(result);
             }
             else
             {
-                var result = _liveTvManager.GetNewTimerDefaults(request.ProgramId, CancellationToken.None).Result;
+                var result = await _liveTvManager.GetNewTimerDefaults(request.ProgramId, CancellationToken.None).ConfigureAwait(false);
 
                 return ToOptimizedSerializedResultUsingCache(result);
             }
         }
 
-        public object Get(GetProgram request)
+        public async Task<object> Get(GetProgram request)
         {
             var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(new Guid(request.UserId));
 
-            var result = _liveTvManager.GetProgram(request.Id, CancellationToken.None, user).Result;
+            var result = await _liveTvManager.GetProgram(request.Id, CancellationToken.None, user).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
@@ -539,23 +539,23 @@ namespace MediaBrowser.Api.LiveTv
             Task.WaitAll(task);
         }
 
-        public object Get(GetRecordingGroups request)
+        public async Task<object> Get(GetRecordingGroups request)
         {
-            var result = _liveTvManager.GetRecordingGroups(new RecordingGroupQuery
+            var result = await _liveTvManager.GetRecordingGroups(new RecordingGroupQuery
             {
                 UserId = request.UserId
 
-            }, CancellationToken.None).Result;
+            }, CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
 
-        public object Get(GetRecordingGroup request)
+        public async Task<object> Get(GetRecordingGroup request)
         {
-            var result = _liveTvManager.GetRecordingGroups(new RecordingGroupQuery
+            var result = await _liveTvManager.GetRecordingGroups(new RecordingGroupQuery
             {
 
-            }, CancellationToken.None).Result;
+            }, CancellationToken.None).ConfigureAwait(false);
 
             var group = result.Items.FirstOrDefault(i => string.Equals(i.Id, request.Id, StringComparison.OrdinalIgnoreCase));
 

+ 3 - 4
MediaBrowser.Api/Movies/CollectionService.cs

@@ -59,17 +59,16 @@ namespace MediaBrowser.Api.Movies
             _dtoService = dtoService;
         }
 
-        public object Post(CreateCollection request)
+        public async Task<object> Post(CreateCollection request)
         {
-            var task = _collectionManager.CreateCollection(new CollectionCreationOptions
+            var item = await _collectionManager.CreateCollection(new CollectionCreationOptions
             {
                 IsLocked = request.IsLocked,
                 Name = request.Name,
                 ParentId = request.ParentId,
                 ItemIdList = (request.Ids ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToList()
-            });
 
-            var item = task.Result;
+            }).ConfigureAwait(false);
 
             var dto = _dtoService.GetBaseItemDto(item, new List<ItemFields>());
 

+ 3 - 7
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -468,11 +468,9 @@ namespace MediaBrowser.Api.Playback
         /// <param name="state">The state.</param>
         /// <param name="outputVideoCodec">The output video codec.</param>
         /// <param name="allowTimeStampCopy">if set to <c>true</c> [allow time stamp copy].</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>System.String.</returns>
         protected string GetOutputSizeParam(StreamState state,
             string outputVideoCodec,
-            CancellationToken cancellationToken,
             bool allowTimeStampCopy = true)
         {
             // http://sonnati.wordpress.com/2012/10/19/ffmpeg-the-swiss-army-knife-of-internet-streaming-part-vi/
@@ -562,7 +560,7 @@ namespace MediaBrowser.Api.Playback
 
             if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream)
             {
-                var subParam = GetTextSubtitleParam(state, cancellationToken);
+                var subParam = GetTextSubtitleParam(state);
 
                 filters.Add(subParam);
 
@@ -584,10 +582,8 @@ namespace MediaBrowser.Api.Playback
         /// Gets the text subtitle param.
         /// </summary>
         /// <param name="state">The state.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>System.String.</returns>
-        protected string GetTextSubtitleParam(StreamState state,
-            CancellationToken cancellationToken)
+        protected string GetTextSubtitleParam(StreamState state)
         {
             var seconds = Math.Round(TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds);
 
@@ -635,7 +631,7 @@ namespace MediaBrowser.Api.Playback
             // Add resolution params, if specified
             if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
             {
-                outputSizeParam = GetOutputSizeParam(state, outputVideoCodec, CancellationToken.None).TrimEnd('"');
+                outputSizeParam = GetOutputSizeParam(state, outputVideoCodec).TrimEnd('"');
                 outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
             }
 

+ 33 - 13
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -20,7 +20,12 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Api.Playback.Hls
 {
+    /// <summary>
+    /// Options is needed for chromecast. Threw Head in there since it's related
+    /// </summary>
     [Route("/Videos/{Id}/master.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")]
+    [Route("/Videos/{Id}/master.m3u8", "HEAD", Summary = "Gets a video stream using HTTP live streaming.")]
+    [Route("/Videos/{Id}/master.m3u8", "OPTIONS", Summary = "Gets a video stream using HTTP live streaming.")]
     public class GetMasterHlsVideoStream : VideoStreamRequest
     {
         public bool EnableAdaptiveBitrateStreaming { get; set; }
@@ -61,7 +66,21 @@ namespace MediaBrowser.Api.Playback.Hls
 
         public object Get(GetMasterHlsVideoStream request)
         {
-            var result = GetAsync(request).Result;
+            var result = GetAsync(request, "GET").Result;
+
+            return result;
+        }
+
+        public object Options(GetMasterHlsVideoStream request)
+        {
+            var result = GetAsync(request, "OPTIONS").Result;
+
+            return result;
+        }
+
+        public object Head(GetMasterHlsVideoStream request)
+        {
+            var result = GetAsync(request, "HEAD").Result;
 
             return result;
         }
@@ -70,10 +89,6 @@ namespace MediaBrowser.Api.Playback.Hls
         {
             var result = GetPlaylistAsync(request, "main").Result;
 
-            // Get the transcoding started
-            //var start = GetStartNumber(request);
-            //var segment = GetDynamicSegment(request, start.ToString(UsCulture)).Result;
-
             return result;
         }
 
@@ -319,7 +334,7 @@ namespace MediaBrowser.Api.Playback.Hls
             return job != null && !job.HasExited;
         }
 
-        private async Task<object> GetAsync(GetMasterHlsVideoStream request)
+        private async Task<object> GetAsync(GetMasterHlsVideoStream request, string method)
         {
             var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
 
@@ -338,10 +353,15 @@ namespace MediaBrowser.Api.Playback.Hls
                 throw new ArgumentException("MediaSourceId is required");
             }
 
-            var audioBitrate = state.OutputAudioBitrate ?? 0;
-            var videoBitrate = state.OutputVideoBitrate ?? 0;
+            var playlistText = string.Empty;
+
+            if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase))
+            {
+                var audioBitrate = state.OutputAudioBitrate ?? 0;
+                var videoBitrate = state.OutputVideoBitrate ?? 0;
 
-            var playlistText = GetMasterPlaylistFileText(state, videoBitrate + audioBitrate);
+                playlistText = GetMasterPlaylistFileText(state, videoBitrate + audioBitrate);
+            }
 
             return ResultFactory.GetResult(playlistText, Common.Net.MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
         }
@@ -359,14 +379,14 @@ namespace MediaBrowser.Api.Playback.Hls
             var playlistUrl = (state.RunTimeTicks ?? 0) > 0 ? "main.m3u8" : "live.m3u8";
             playlistUrl += queryString;
 
-            var request = (GetMasterHlsVideoStream) state.Request;
+            var request = (GetMasterHlsVideoStream)state.Request;
 
             var subtitleStreams = state.AllMediaStreams
                 .Where(i => i.IsTextSubtitleStream)
                 .ToList();
 
-            var subtitleGroup = subtitleStreams.Count > 0 && request.SubtitleMethod == SubtitleDeliveryMethod.Hls ? 
-                "subs" : 
+            var subtitleGroup = subtitleStreams.Count > 0 && request.SubtitleMethod == SubtitleDeliveryMethod.Hls ?
+                "subs" :
                 null;
 
             AppendPlaylist(builder, playlistUrl, totalBitrate, subtitleGroup);
@@ -582,7 +602,7 @@ namespace MediaBrowser.Api.Playback.Hls
             // Add resolution params, if specified
             if (!hasGraphicalSubs)
             {
-                args += GetOutputSizeParam(state, codec, CancellationToken.None, false);
+                args += GetOutputSizeParam(state, codec, false);
             }
 
             // This is for internal graphical subs

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

@@ -180,7 +180,7 @@ namespace MediaBrowser.Api.Playback.Hls
             // Add resolution params, if specified
             if (!hasGraphicalSubs)
             {
-                args += GetOutputSizeParam(state, codec, CancellationToken.None);
+                args += GetOutputSizeParam(state, codec);
             }
 
             // This is for internal graphical subs

+ 12 - 0
MediaBrowser.Api/Playback/Progressive/AudioService.cs

@@ -20,6 +20,8 @@ namespace MediaBrowser.Api.Playback.Progressive
     [Route("/Audio/{Id}/stream", "GET", Summary = "Gets an audio stream")]
     [Route("/Audio/{Id}/stream.{Container}", "HEAD", Summary = "Gets an audio stream")]
     [Route("/Audio/{Id}/stream", "HEAD", Summary = "Gets an audio stream")]
+    [Route("/Audio/{Id}/stream.{Container}", "OPTIONS", Summary = "Gets an audio stream")]
+    [Route("/Audio/{Id}/stream", "OPTIONS", Summary = "Gets an audio stream")]
     public class GetAudioStream : StreamRequest
     {
         [ApiMember(Name = "Container", Description = "Container", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
@@ -55,6 +57,16 @@ namespace MediaBrowser.Api.Playback.Progressive
             return ProcessRequest(request, true);
         }
 
+        /// <summary>
+        /// Gets the specified request.
+        /// </summary>
+        /// <param name="request">The request.</param>
+        /// <returns>System.Object.</returns>
+        public object Options(GetAudioStream request)
+        {
+            return ProcessRequest(request, true);
+        }
+
         /// <summary>
         /// Gets the command line arguments.
         /// </summary>

+ 35 - 22
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -114,7 +114,9 @@ namespace MediaBrowser.Api.Playback.Progressive
         /// <returns>Task.</returns>
         protected object ProcessRequest(StreamRequest request, bool isHeadRequest)
         {
-            var state = GetState(request, CancellationToken.None).Result;
+            var cancellationTokenSource = new CancellationTokenSource();
+
+            var state = GetState(request, cancellationTokenSource.Token).Result;
 
             var responseHeaders = new Dictionary<string, string>();
 
@@ -125,7 +127,7 @@ namespace MediaBrowser.Api.Playback.Progressive
 
                 using (state)
                 {
-                    return GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest).Result;
+                    return GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).Result;
                 }
             }
 
@@ -171,7 +173,7 @@ namespace MediaBrowser.Api.Playback.Progressive
             // Need to start ffmpeg
             try
             {
-                return GetStreamResult(state, responseHeaders, isHeadRequest).Result;
+                return GetStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).Result;
             }
             catch
             {
@@ -187,8 +189,9 @@ namespace MediaBrowser.Api.Playback.Progressive
         /// <param name="state">The state.</param>
         /// <param name="responseHeaders">The response headers.</param>
         /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
+        /// <param name="cancellationTokenSource">The cancellation token source.</param>
         /// <returns>Task{System.Object}.</returns>
-        private async Task<object> GetStaticRemoteStreamResult(StreamState state, Dictionary<string, string> responseHeaders, bool isHeadRequest)
+        private async Task<object> GetStaticRemoteStreamResult(StreamState state, Dictionary<string, string> responseHeaders, bool isHeadRequest, CancellationTokenSource cancellationTokenSource)
         {
             string useragent = null;
             state.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent);
@@ -197,7 +200,8 @@ namespace MediaBrowser.Api.Playback.Progressive
             {
                 Url = state.MediaPath,
                 UserAgent = useragent,
-                BufferContent = false
+                BufferContent = false,
+                CancellationToken = cancellationTokenSource.Token
             };
 
             var response = await HttpClient.GetResponse(options).ConfigureAwait(false);
@@ -238,8 +242,9 @@ namespace MediaBrowser.Api.Playback.Progressive
         /// <param name="state">The state.</param>
         /// <param name="responseHeaders">The response headers.</param>
         /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
+        /// <param name="cancellationTokenSource">The cancellation token source.</param>
         /// <returns>Task{System.Object}.</returns>
-        private async Task<object> GetStreamResult(StreamState state, IDictionary<string, string> responseHeaders, bool isHeadRequest)
+        private async Task<object> GetStreamResult(StreamState state, IDictionary<string, string> responseHeaders, bool isHeadRequest, CancellationTokenSource cancellationTokenSource)
         {
             // Use the command line args with a dummy playlist path
             var outputPath = state.OutputFilePath;
@@ -275,28 +280,36 @@ namespace MediaBrowser.Api.Playback.Progressive
                 return streamResult;
             }
 
-            if (!File.Exists(outputPath))
-            {
-                await StartFfMpeg(state, outputPath, new CancellationTokenSource()).ConfigureAwait(false);
-            }
-            else
+            await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
+            try
             {
-                ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
-                state.Dispose();
-            }
+                if (!File.Exists(outputPath))
+                {
+                    await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false);
+                }
+                else
+                {
+                    ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
+                    state.Dispose();
+                }
 
-            var job = ApiEntryPoint.Instance.GetTranscodingJob(outputPath, TranscodingJobType.Progressive);
-            var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem, job);
+                var job = ApiEntryPoint.Instance.GetTranscodingJob(outputPath, TranscodingJobType.Progressive);
+                var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem, job);
 
-            result.Options["Content-Type"] = contentType;
+                result.Options["Content-Type"] = contentType;
 
-            // Add the response headers to the result object
-            foreach (var item in responseHeaders)
+                // Add the response headers to the result object
+                foreach (var item in responseHeaders)
+                {
+                    result.Options[item.Key] = item.Value;
+                }
+
+                return result;
+            }
+            finally
             {
-                result.Options[item.Key] = item.Value;
+                ApiEntryPoint.Instance.TranscodingStartLock.Release();
             }
-
-            return result;
         }
 
         /// <summary>

+ 4 - 3
MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Common.IO;
+using System;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Model.Logging;
 using ServiceStack.Web;
 using System.Collections.Generic;
@@ -64,9 +65,9 @@ namespace MediaBrowser.Api.Playback.Progressive
                 await new ProgressiveFileCopier(_fileSystem, _job)
                     .StreamFile(Path, responseStream).ConfigureAwait(false);
             }
-            catch
+            catch (Exception ex)
             {
-                Logger.Error("Error streaming media. The client has most likely disconnected or transcoding has failed.");
+                Logger.ErrorException("Error streaming media. The client has most likely disconnected or transcoding has failed.", ex);
 
                 throw;
             }

+ 17 - 2
MediaBrowser.Api/Playback/Progressive/VideoService.cs

@@ -11,7 +11,6 @@ using MediaBrowser.Model.IO;
 using ServiceStack;
 using System;
 using System.IO;
-using System.Threading;
 
 namespace MediaBrowser.Api.Playback.Progressive
 {
@@ -50,6 +49,12 @@ namespace MediaBrowser.Api.Playback.Progressive
     [Route("/Videos/{Id}/stream.wtv", "HEAD")]
     [Route("/Videos/{Id}/stream.m2ts", "HEAD")]
     [Route("/Videos/{Id}/stream", "HEAD")]
+    [Route("/Videos/{Id}/stream.mp4", "OPTIONS")]
+    [Route("/Videos/{Id}/stream.m4v", "OPTIONS")]
+    [Route("/Videos/{Id}/stream.mkv", "OPTIONS")]
+    [Route("/Videos/{Id}/stream.avi", "OPTIONS")]
+    [Route("/Videos/{Id}/stream.webm", "OPTIONS")]
+    [Route("/Videos/{Id}/stream.ts", "OPTIONS")]
     [Api(Description = "Gets a video stream")]
     public class GetVideoStream : VideoStreamRequest
     {
@@ -85,6 +90,16 @@ namespace MediaBrowser.Api.Playback.Progressive
             return ProcessRequest(request, true);
         }
 
+        /// <summary>
+        /// Optionses the specified request.
+        /// </summary>
+        /// <param name="request">The request.</param>
+        /// <returns>System.Object.</returns>
+        public object Options(GetVideoStream request)
+        {
+            return ProcessRequest(request, true);
+        }
+
         /// <summary>
         /// Gets the command line arguments.
         /// </summary>
@@ -154,7 +169,7 @@ namespace MediaBrowser.Api.Playback.Progressive
             // Add resolution params, if specified
             if (!hasGraphicalSubs)
             {
-                args += GetOutputSizeParam(state, codec, CancellationToken.None);
+                args += GetOutputSizeParam(state, codec);
             }
 
             var qualityParam = GetVideoQualityParam(state, codec, false);

+ 3 - 4
MediaBrowser.Api/PlaylistService.cs

@@ -97,16 +97,15 @@ namespace MediaBrowser.Api
             _libraryManager = libraryManager;
         }
 
-        public object Post(CreatePlaylist request)
+        public async Task<object> Post(CreatePlaylist request)
         {
-            var task = _playlistManager.CreatePlaylist(new PlaylistCreationOptions
+            var item = await _playlistManager.CreatePlaylist(new PlaylistCreationOptions
             {
                 Name = request.Name,
                 ItemIdList = (request.Ids ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(),
                 UserId = request.UserId
-            });
 
-            var item = task.Result;
+            }).ConfigureAwait(false);
 
             var dto = _dtoService.GetBaseItemDto(item, new List<ItemFields>());
 

+ 2 - 2
MediaBrowser.Api/SearchService.cs

@@ -111,9 +111,9 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <param name="request">The request.</param>
         /// <returns>System.Object.</returns>
-        public object Get(GetSearchHints request)
+        public async Task<object> Get(GetSearchHints request)
         {
-            var result = GetSearchHintsAsync(request).Result;
+            var result = await GetSearchHintsAsync(request).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }

+ 4 - 6
MediaBrowser.Api/Sync/SyncService.cs

@@ -87,16 +87,14 @@ namespace MediaBrowser.Api.Sync
             return ToOptimizedResult(result);
         }
 
-        public void Delete(CancelSyncJob request)
+        public Task Delete(CancelSyncJob request)
         {
-            var task = _syncManager.CancelJob(request.Id);
-
-            Task.WaitAll(task);
+            return _syncManager.CancelJob(request.Id);
         }
 
-        public object Post(CreateSyncJob request)
+        public async Task<object> Post(CreateSyncJob request)
         {
-            var result = _syncManager.CreateJob(request);
+            var result = await _syncManager.CreateJob(request).ConfigureAwait(false);
 
             return ToOptimizedResult(result);
         }

+ 14 - 27
MediaBrowser.Api/UserLibrary/PlaystateService.cs

@@ -221,14 +221,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// Posts the specified request.
         /// </summary>
         /// <param name="request">The request.</param>
-        public object Post(MarkPlayedItem request)
-        {
-            var result = MarkPlayed(request).Result;
-
-            return ToOptimizedResult(result);
-        }
-
-        private async Task<UserItemDataDto> MarkPlayed(MarkPlayedItem request)
+        public async Task<object> Post(MarkPlayedItem request)
         {
             var user = _userManager.GetUserById(request.UserId);
 
@@ -250,18 +243,18 @@ namespace MediaBrowser.Api.UserLibrary
                 await UpdatePlayedStatus(additionalUser, request.Id, true, datePlayed).ConfigureAwait(false);
             }
 
-            return dto;
+            return ToOptimizedResult(dto);
         }
 
         /// <summary>
         /// Posts the specified request.
         /// </summary>
         /// <param name="request">The request.</param>
-        public void Post(OnPlaybackStart request)
+        public Task Post(OnPlaybackStart request)
         {
             var queueableMediaTypes = (request.QueueableMediaTypes ?? string.Empty);
 
-            Post(new ReportPlaybackStart
+            return Post(new ReportPlaybackStart
             {
                 CanSeek = request.CanSeek,
                 ItemId = request.Id,
@@ -272,22 +265,20 @@ namespace MediaBrowser.Api.UserLibrary
             });
         }
 
-        public void Post(ReportPlaybackStart request)
+        public Task Post(ReportPlaybackStart request)
         {
             request.SessionId = GetSession().Id;
 
-            var task = _sessionManager.OnPlaybackStart(request);
-
-            Task.WaitAll(task);
+            return _sessionManager.OnPlaybackStart(request);
         }
 
         /// <summary>
         /// Posts the specified request.
         /// </summary>
         /// <param name="request">The request.</param>
-        public void Post(OnPlaybackProgress request)
+        public Task Post(OnPlaybackProgress request)
         {
-            Post(new ReportPlaybackProgress
+            return Post(new ReportPlaybackProgress
             {
                 ItemId = request.Id,
                 PositionTicks = request.PositionTicks,
@@ -300,22 +291,20 @@ namespace MediaBrowser.Api.UserLibrary
             });
         }
 
-        public void Post(ReportPlaybackProgress request)
+        public Task Post(ReportPlaybackProgress request)
         {
             request.SessionId = GetSession().Id;
 
-            var task = _sessionManager.OnPlaybackProgress(request);
-
-            Task.WaitAll(task);
+            return _sessionManager.OnPlaybackProgress(request);
         }
 
         /// <summary>
         /// Posts the specified request.
         /// </summary>
         /// <param name="request">The request.</param>
-        public void Delete(OnPlaybackStopped request)
+        public Task Delete(OnPlaybackStopped request)
         {
-            Post(new ReportPlaybackStopped
+            return Post(new ReportPlaybackStopped
             {
                 ItemId = request.Id,
                 PositionTicks = request.PositionTicks,
@@ -323,13 +312,11 @@ namespace MediaBrowser.Api.UserLibrary
             });
         }
 
-        public void Post(ReportPlaybackStopped request)
+        public Task Post(ReportPlaybackStopped request)
         {
             request.SessionId = GetSession().Id;
 
-            var task = _sessionManager.OnPlaybackStopped(request);
-
-            Task.WaitAll(task);
+            return _sessionManager.OnPlaybackStopped(request);
         }
 
         /// <summary>

+ 2 - 2
MediaBrowser.Api/UserLibrary/UserLibraryService.cs

@@ -400,7 +400,7 @@ namespace MediaBrowser.Api.UserLibrary
             return ToOptimizedResult(dtos.ToList());
         }
 
-        public object Get(GetUserViews request)
+        public async Task<object> Get(GetUserViews request)
         {
             var user = _userManager.GetUserById(new Guid(request.UserId));
 
@@ -418,7 +418,7 @@ namespace MediaBrowser.Api.UserLibrary
                 query.IncludeExternalContent = request.IncludeExternalContent.Value;
             }
 
-            var folders = _userViewManager.GetUserViews(query, CancellationToken.None).Result;
+            var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false);
 
             var dtos = folders.OrderBy(i => i.SortName)
                 .Select(i => _dtoService.GetBaseItemDto(i, fields, user))

+ 13 - 25
MediaBrowser.Api/UserService.cs

@@ -12,7 +12,6 @@ using ServiceStack.Text.Controller;
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Net;
 using System.Threading.Tasks;
 
 namespace MediaBrowser.Api
@@ -275,7 +274,7 @@ namespace MediaBrowser.Api
         /// Deletes the specified request.
         /// </summary>
         /// <param name="request">The request.</param>
-        public void Delete(DeleteUser request)
+        public async Task Delete(DeleteUser request)
         {
             var user = _userManager.GetUserById(request.Id);
 
@@ -284,13 +283,8 @@ namespace MediaBrowser.Api
                 throw new ResourceNotFoundException("User not found");
             }
 
-            var revokeTask = _sessionMananger.RevokeUserTokens(user.Id.ToString("N"));
-
-            Task.WaitAll(revokeTask);
-
-            var task = _userManager.DeleteUser(user);
-
-            Task.WaitAll(task);
+            await _sessionMananger.RevokeUserTokens(user.Id.ToString("N")).ConfigureAwait(false);
+            await _userManager.DeleteUser(user).ConfigureAwait(false);
         }
 
         /// <summary>
@@ -313,7 +307,7 @@ namespace MediaBrowser.Api
             });
         }
 
-        public object Post(AuthenticateUserByName request)
+        public async Task<object> Post(AuthenticateUserByName request)
         {
             var auth = AuthorizationContext.GetAuthorizationInfo(Request);
 
@@ -334,7 +328,7 @@ namespace MediaBrowser.Api
                 auth.DeviceId = "Unknown device id";
             }
 
-            var result = _sessionMananger.AuthenticateNewSession(new AuthenticationRequest
+            var result = await _sessionMananger.AuthenticateNewSession(new AuthenticationRequest
             {
                 App = auth.Client,
                 AppVersion = auth.Version,
@@ -344,7 +338,7 @@ namespace MediaBrowser.Api
                 RemoteEndPoint = Request.RemoteIp,
                 Username = request.Username
 
-            }, Request.IsLocal).Result;
+            }, Request.IsLocal).ConfigureAwait(false);
 
             return ToOptimizedResult(result);
         }
@@ -353,7 +347,7 @@ namespace MediaBrowser.Api
         /// Posts the specified request.
         /// </summary>
         /// <param name="request">The request.</param>
-        public void Post(UpdateUserPassword request)
+        public async Task Post(UpdateUserPassword request)
         {
             var user = _userManager.GetUserById(request.Id);
 
@@ -364,22 +358,18 @@ namespace MediaBrowser.Api
 
             if (request.ResetPassword)
             {
-                var task = _userManager.ResetPassword(user);
-
-                Task.WaitAll(task);
+                await _userManager.ResetPassword(user).ConfigureAwait(false);
             }
             else
             {
-                var success = _userManager.AuthenticateUser(user.Name, request.CurrentPassword, Request.RemoteIp).Result;
+                var success = await _userManager.AuthenticateUser(user.Name, request.CurrentPassword, Request.RemoteIp).ConfigureAwait(false);
 
                 if (!success)
                 {
                     throw new ArgumentException("Invalid user or password entered.");
                 }
 
-                var task = _userManager.ChangePassword(user, request.NewPassword);
-
-                Task.WaitAll(task);
+                await _userManager.ChangePassword(user, request.NewPassword).ConfigureAwait(false);
             }
         }
 
@@ -387,7 +377,7 @@ namespace MediaBrowser.Api
         /// Posts the specified request.
         /// </summary>
         /// <param name="request">The request.</param>
-        public void Post(UpdateUser request)
+        public async Task Post(UpdateUser request)
         {
             // We need to parse this manually because we told service stack not to with IRequiresRequestStream
             // https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
@@ -421,16 +411,14 @@ namespace MediaBrowser.Api
                     throw new ArgumentException("There must be at least one enabled user in the system.");
                 }
 
-                var revokeTask = _sessionMananger.RevokeUserTokens(user.Id.ToString("N"));
-
-                Task.WaitAll(revokeTask);
+                await _sessionMananger.RevokeUserTokens(user.Id.ToString("N")).ConfigureAwait(false);
             }
 
             var task = user.Name.Equals(dtoUser.Name, StringComparison.Ordinal) ?
                 _userManager.UpdateUser(user) :
                 _userManager.RenameUser(user, dtoUser.Name);
 
-            Task.WaitAll(task);
+            await task.ConfigureAwait(false);
 
             user.UpdateConfiguration(dtoUser.Configuration);
         }

+ 6 - 20
MediaBrowser.Api/VideosService.cs

@@ -97,14 +97,7 @@ namespace MediaBrowser.Api
             return ToOptimizedSerializedResultUsingCache(result);
         }
 
-        public void Delete(DeleteAlternateSources request)
-        {
-            var task = RemoveAlternateVersions(request);
-
-            Task.WaitAll(task);
-        }
-
-        private async Task RemoveAlternateVersions(DeleteAlternateSources request)
+        public async Task Delete(DeleteAlternateSources request)
         {
             var video = (Video)_libraryManager.GetItemById(request.Id);
 
@@ -119,14 +112,7 @@ namespace MediaBrowser.Api
             await video.UpdateToRepository(ItemUpdateType.MetadataDownload, CancellationToken.None).ConfigureAwait(false);
         }
 
-        public void Post(MergeVersions request)
-        {
-            var task = MergeVersions(request);
-
-            Task.WaitAll(task);
-        }
-
-        private async Task MergeVersions(MergeVersions request)
+        public async Task Post(MergeVersions request)
         {
             var items = request.Ids.Split(',')
                 .Select(i => new Guid(i))
@@ -172,12 +158,12 @@ namespace MediaBrowser.Api
                     return 0;
                 })
                     .ThenByDescending(i =>
-                {
-                    var stream = i.GetDefaultVideoStream();
+                    {
+                        var stream = i.GetDefaultVideoStream();
 
-                    return stream == null || stream.Width == null ? 0 : stream.Width.Value;
+                        return stream == null || stream.Width == null ? 0 : stream.Width.Value;
 
-                }).First();
+                    }).First();
             }
 
             foreach (var item in videos.Where(i => i.Id != primaryVersion.Id))

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

@@ -112,12 +112,12 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
         private IEnumerable<KeyValuePair<string, string>> HandleGetSearchCapabilities()
         {
-            return new Headers(true) { { "SearchCaps", "upnp:class,dc:title,upnp:artist" } };
+            return new Headers(true) { { "SearchCaps", "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords" } };
         }
 
         private IEnumerable<KeyValuePair<string, string>> HandleGetSortCapabilities()
         {
-            return new Headers(true) { { "SortCaps", string.Empty } };
+            return new Headers(true) { { "SortCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" } };
         }
 
         private IEnumerable<KeyValuePair<string, string>> HandleGetSystemUpdateID()
@@ -317,14 +317,10 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
         private async Task<QueryResult<BaseItem>> GetChildrenSorted(Folder folder, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
         {
-            if (search.SearchType == SearchType.Unknown)
-            {
-                return await GetChildrenSorted(folder, user, sort, startIndex, limit).ConfigureAwait(false);
-            }
-
-            var result = await GetChildrenSorted(folder, user, sort, null, null).ConfigureAwait(false);
+            // TODO: Make a recursive version of GetChildrenSorted (although sorting isn't needed)
+            var result = folder.GetRecursiveChildren(user, true);
 
-            var items = FilterUnsupportedContent(result.Items);
+            var items = FilterUnsupportedContent(result);
 
             if (search.SearchType == SearchType.Audio)
             {
@@ -342,6 +338,10 @@ namespace MediaBrowser.Dlna.ContentDirectory
             {
                 items = items.OfType<Playlist>();
             }
+            else if (search.SearchType == SearchType.MusicAlbum)
+            {
+                items = items.OfType<MusicAlbum>();
+            }
 
             items = SortItems(items, user, sort);
 

+ 34 - 2
MediaBrowser.Dlna/Didl/DidlBuilder.cs

@@ -6,6 +6,7 @@ using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Entities;
@@ -496,6 +497,10 @@ namespace MediaBrowser.Dlna.Didl
                     {
                         classType = "object.container.album.videoAlbum";
                     }
+                    else if (item is Playlist)
+                    {
+                        classType = "object.container.playlistContainer";
+                    }
                 }
 
                 objectClass.InnerText = classType ?? "object.container.storageFolder";
@@ -560,7 +565,7 @@ namespace MediaBrowser.Dlna.Didl
 
                 foreach (var artist in audio.AlbumArtists)
                 {
-                    AddValue(element, "upnp", "albumArtist", artist, NS_UPNP);
+                    AddAlbumArtist(element, artist);
                 }
             }
 
@@ -569,9 +574,13 @@ namespace MediaBrowser.Dlna.Didl
             if (album != null)
             {
                 foreach (var artist in album.AlbumArtists)
+                {
+                    AddAlbumArtist(element, artist);
+                    AddValue(element, "upnp", "artist", artist, NS_UPNP);
+                }
+                foreach (var artist in album.Artists)
                 {
                     AddValue(element, "upnp", "artist", artist, NS_UPNP);
-                    AddValue(element, "upnp", "albumArtist", artist, NS_UPNP);
                 }
             }
 
@@ -582,6 +591,7 @@ namespace MediaBrowser.Dlna.Didl
                 if (!string.IsNullOrEmpty(musicVideo.Artist))
                 {
                     AddValue(element, "upnp", "artist", musicVideo.Artist, NS_UPNP);
+                    AddAlbumArtist(element, musicVideo.Artist);
                 }
 
                 if (!string.IsNullOrEmpty(musicVideo.Album))
@@ -593,6 +603,28 @@ namespace MediaBrowser.Dlna.Didl
             if (item.IndexNumber.HasValue)
             {
                 AddValue(element, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
+
+                if (item is Episode)
+                {
+                    AddValue(element, "upnp", "episodeNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
+                }
+            }
+        }
+
+        private void AddAlbumArtist(XmlElement elem, string name)
+        {
+            try
+            {
+                var newNode = elem.OwnerDocument.CreateElement("upnp", "artist", NS_UPNP);
+                newNode.InnerText = name;
+
+                newNode.SetAttribute("role", "AlbumArtist");
+
+                elem.AppendChild(newNode);
+            }
+            catch (XmlException)
+            {
+                //_logger.Error("Error adding xml value: " + value);
             }
         }
 

+ 0 - 3
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -180,9 +180,6 @@ namespace MediaBrowser.Model.Configuration
 
         public SubtitleOptions SubtitleOptions { get; set; }
 
-        public ChannelOptions ChannelOptions { get; set; }
-        public ChapterOptions ChapterOptions { get; set; }
-
         public bool DefaultMetadataSettingsApplied { get; set; }
 
         public bool EnableTokenAuthentication { get; set; }

+ 26 - 16
MediaBrowser.Model/Dlna/SearchCriteria.cs

@@ -16,24 +16,34 @@ namespace MediaBrowser.Model.Dlna
 
             SearchType = SearchType.Unknown;
 
-            if (StringHelper.IndexOfIgnoreCase(search, "upnp:class") != -1 &&
-                StringHelper.IndexOfIgnoreCase(search, "derivedfrom") != -1)
+            String[] factors = StringHelper.RegexSplit(search, "(and|or)");
+            foreach (String factor in factors)
             {
-                if (StringHelper.IndexOfIgnoreCase(search, "object.item.audioItem") != -1)
-                {
-                    SearchType = SearchType.Audio;
-                }
-                else if (StringHelper.IndexOfIgnoreCase(search, "object.item.imageItem") != -1)
-                {
-                    SearchType = SearchType.Image;
-                }
-                else if (StringHelper.IndexOfIgnoreCase(search, "object.item.videoItem") != -1)
-                {
-                    SearchType = SearchType.Video;
-                }
-                else if (StringHelper.IndexOfIgnoreCase(search, "object.container.playlistContainer") != -1)
+                String[] subFactors = StringHelper.RegexSplit(factor.Trim().Trim('(').Trim(')').Trim(), "\\s", 3);
+
+                if (subFactors.Length == 3)
                 {
-                    SearchType = SearchType.Playlist;
+
+                    if (StringHelper.EqualsIgnoreCase("upnp:class", subFactors[0]) && 
+                        (StringHelper.EqualsIgnoreCase("=", subFactors[1]) || StringHelper.EqualsIgnoreCase("derivedfrom", subFactors[1])))
+                    {
+                        if (StringHelper.EqualsIgnoreCase("\"object.item.imageItem\"", subFactors[2]) || StringHelper.EqualsIgnoreCase("\"object.item.imageItem.photo\"", subFactors[2]))
+                        {
+                            SearchType = SearchType.Image;
+                        }
+                        else if (StringHelper.EqualsIgnoreCase("\"object.item.videoItem\"", subFactors[2]))
+                        {
+                            SearchType = SearchType.Video;
+                        }
+                        else if (StringHelper.EqualsIgnoreCase("\"object.container.playlistContainer\"", subFactors[2]))
+                        {
+                            SearchType = SearchType.Playlist;
+                        }
+                        else if (StringHelper.EqualsIgnoreCase("\"object.container.album.musicAlbum\"", subFactors[2]))
+                        {
+                            SearchType = SearchType.MusicAlbum;
+                        }
+                    }
                 }
             }
         }

+ 2 - 1
MediaBrowser.Model/Dlna/SearchType.cs

@@ -6,6 +6,7 @@
         Audio = 1,
         Image = 2,
         Video = 3,
-        Playlist = 4
+        Playlist = 4,
+        MusicAlbum = 5
     }
 }

+ 0 - 12
MediaBrowser.Model/Dto/BaseItemDto.cs

@@ -347,24 +347,12 @@ namespace MediaBrowser.Model.Dto
         /// <value>The user data.</value>
         public UserItemDataDto UserData { get; set; }
 
-        /// <summary>
-        /// Gets or sets the played percentage.
-        /// </summary>
-        /// <value>The played percentage.</value>
-        public double? PlayedPercentage { get; set; }
-
         /// <summary>
         /// Gets or sets the recursive item count.
         /// </summary>
         /// <value>The recursive item count.</value>
         public int? RecursiveItemCount { get; set; }
 
-        /// <summary>
-        /// Gets or sets the recursive unplayed item count.
-        /// </summary>
-        /// <value>The recursive unplayed item count.</value>
-        public int? RecursiveUnplayedItemCount { get; set; }
-
         /// <summary>
         /// Gets or sets the child count.
         /// </summary>

+ 24 - 0
MediaBrowser.Model/Extensions/StringHelper.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Globalization;
+using System.Text.RegularExpressions;
 
 namespace MediaBrowser.Model.Extensions
 {
@@ -70,5 +71,28 @@ namespace MediaBrowser.Model.Extensions
         {
             return str.TrimStart(c);
         }
+
+        /// <summary>
+        /// Splits the specified string.
+        /// </summary>
+        /// <param name="str">The string.</param>
+        /// <param name="term">The term.</param>
+        /// <returns>System.String[].</returns>
+        public static string[] RegexSplit(string str, string term)
+        {
+            return Regex.Split(str, term, RegexOptions.IgnoreCase);
+        }
+
+        /// <summary>
+        /// Splits the specified string.
+        /// </summary>
+        /// <param name="str">The string.</param>
+        /// <param name="term">The term.</param>
+        /// <param name="limit">The limit.</param>
+        /// <returns>System.String[].</returns>
+        public static string[] RegexSplit(string str, string term, int limit)
+        {
+            return new Regex(term).Split(str, limit);
+        }
     }
 }

+ 0 - 8
MediaBrowser.Model/Logging/ILogger.cs

@@ -51,14 +51,6 @@ namespace MediaBrowser.Model.Logging
         /// <param name="paramList">The param list.</param>
         void FatalException(string message, Exception exception, params object[] paramList);
         
-        /// <summary>
-        /// Logs the specified severity.
-        /// </summary>
-        /// <param name="severity">The severity.</param>
-        /// <param name="message">The message.</param>
-        /// <param name="paramList">The param list.</param>
-        void Log(LogSeverity severity, string message, params object[] paramList);
-
         /// <summary>
         /// Logs the exception.
         /// </summary>

+ 3 - 5
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -256,9 +256,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                     SetSpecialCounts(folder, user, dto, fields);
                 }
 
-                dto.UserData.Played = dto.PlayedPercentage.HasValue && dto.PlayedPercentage.Value >= 100;
-                dto.UserData.PlayedPercentage = dto.PlayedPercentage;
-                dto.UserData.UnplayedItemCount = dto.RecursiveUnplayedItemCount;
+                dto.UserData.Played = dto.UserData.PlayedPercentage.HasValue && dto.UserData.PlayedPercentage.Value >= 100;
             }
 
             else
@@ -1365,11 +1363,11 @@ namespace MediaBrowser.Server.Implementations.Dto
             }
 
             dto.RecursiveItemCount = recursiveItemCount;
-            dto.RecursiveUnplayedItemCount = unplayed;
+            dto.UserData.UnplayedItemCount = unplayed;
 
             if (recursiveItemCount > 0)
             {
-                dto.PlayedPercentage = totalPercentPlayed / recursiveItemCount;
+                dto.UserData.PlayedPercentage = totalPercentPlayed / recursiveItemCount;
             }
 
             if (runtime > 0 && fields.Contains(ItemFields.CumulativeRunTimeTicks))

+ 44 - 44
MediaBrowser.Server.Implementations/Localization/JavaScript/es.json

@@ -231,59 +231,59 @@
     "OptionBlockLiveTvChannels": "Canales de Tv en vivo",
     "OptionBlockChannelContent": "Contenido de canales de Internet",
     "ButtonRevoke": "Revocar",
-    "MessageConfirmRevokeApiKey": "Are you sure you wish to revoke this api key? The application's connection to Media Browser will be abruptly terminated.",
-    "HeaderConfirmRevokeApiKey": "Revoke Api Key",
-    "ValueContainer": "Container: {0}",
-    "ValueAudioCodec": "Audio Codec: {0}",
-    "ValueVideoCodec": "Video Codec: {0}",
+    "MessageConfirmRevokeApiKey": "\u00bfEst\u00e1 seguro de que desea revocar esta clave api? La conexi\u00f3n de la aplicaci\u00f3n con Mediabrowser terminar\u00e1 abruptamente.",
+    "HeaderConfirmRevokeApiKey": "Revocar Clave Api",
+    "ValueContainer": "Contenedor: {0}",
+    "ValueAudioCodec": "Codec de audio: {0}",
+    "ValueVideoCodec": "Codec de video: {0}",
     "ValueCodec": "Codec: {0}",
-    "ValueConditions": "Conditions: {0}",
-    "LabelAll": "All",
-    "HeaderDeleteImage": "Delete Image",
-    "MessageFileNotFound": "File not found.",
-    "MessageFileReadError": "An error occurred reading this file.",
-    "ButtonNextPage": "Next Page",
-    "ButtonPreviousPage": "Previous Page",
-    "ButtonMoveLeft": "Move left",
-    "ButtonMoveRight": "Move right",
-    "ButtonBrowseOnlineImages": "Browse online images",
-    "HeaderDeleteItem": "Delete Item",
-    "ConfirmDeleteItem": "Are you sure you wish to delete this item from your library?",
-    "MessagePleaseEnterNameOrId": "Please enter a name or an external Id.",
-    "MessageValueNotCorrect": "The value entered is not correct. Please try again.",
-    "MessageItemSaved": "Item saved.",
+    "ValueConditions": "Condiciones: {0}",
+    "LabelAll": "Todo",
+    "HeaderDeleteImage": "Borrar imagen",
+    "MessageFileNotFound": "Archivo no encontrado.",
+    "MessageFileReadError": "Ha ocurrido un error leyendo este archivo.",
+    "ButtonNextPage": "P\u00e1gina siguiente",
+    "ButtonPreviousPage": "P\u00e1gina anterior",
+    "ButtonMoveLeft": "Mover izquierda",
+    "ButtonMoveRight": "Mover derecha",
+    "ButtonBrowseOnlineImages": "Navegar im\u00e1genes online",
+    "HeaderDeleteItem": "Borrar elemento",
+    "ConfirmDeleteItem": "\u00bfEst\u00e1 seguro que desea borrar este elemento de su biblioteca?",
+    "MessagePleaseEnterNameOrId": "Introduzca un nombre o un identificador externo.",
+    "MessageValueNotCorrect": "El valor introducido no es correcto. Intentelo de nuevo.",
+    "MessageItemSaved": "Elemento grabado.",
     "OptionEnded": "Finalizado",
     "OptionContinuing": "Continuando",
     "OptionOff": "Apagado",
     "OptionOn": "Encendido",
-    "HeaderFields": "Fields",
-    "HeaderFieldsHelp": "Slide a field to 'off' to lock it and prevent it's data from being changed.",
-    "HeaderLiveTV": "Live TV",
-    "MissingLocalTrailer": "Missing local trailer.",
-    "MissingPrimaryImage": "Missing primary image.",
-    "MissingBackdropImage": "Missing backdrop image.",
-    "MissingLogoImage": "Missing logo image.",
-    "MissingEpisode": "Missing episode.",
-    "OptionScreenshots": "Screenshots",
-    "OptionBackdrops": "Backdrops",
-    "OptionImages": "Images",
-    "OptionKeywords": "Keywords",
-    "OptionTags": "Tags",
-    "OptionStudios": "Studios",
-    "OptionName": "Name",
-    "OptionOverview": "Overview",
-    "OptionGenres": "Genres",
+    "HeaderFields": "Campos",
+    "HeaderFieldsHelp": "Deslice un campo en 'off' para bloquearlo y evitar que la informaci\u00f3n sea cambiada.",
+    "HeaderLiveTV": "Tv en vivo",
+    "MissingLocalTrailer": "Falta trailer local.",
+    "MissingPrimaryImage": "Falta imagen principal.",
+    "MissingBackdropImage": "Falta imagen de fondo.",
+    "MissingLogoImage": "Falta logo.",
+    "MissingEpisode": "Falta episodio.",
+    "OptionScreenshots": "Capturas del pantalla",
+    "OptionBackdrops": "Im\u00e1genes de fondo",
+    "OptionImages": "Im\u00e1genes",
+    "OptionKeywords": "Palabras clave",
+    "OptionTags": "Etiquetas",
+    "OptionStudios": "Estudios",
+    "OptionName": "Nombre",
+    "OptionOverview": "Sinopsis",
+    "OptionGenres": "G\u00e9neros",
     "OptionParentalRating": "Clasificaci\u00f3n parental",
-    "OptionPeople": "People",
+    "OptionPeople": "Gente",
     "OptionRuntime": "Tiempo",
-    "OptionProductionLocations": "Production Locations",
-    "OptionBirthLocation": "Birth Location",
+    "OptionProductionLocations": "Localizaciones de producci\u00f3n",
+    "OptionBirthLocation": "Lugar de nacimiento",
     "LabelAllChannels": "Todos los canales",
-    "LabelLiveProgram": "LIVE",
-    "LabelNewProgram": "NEW",
-    "LabelPremiereProgram": "PREMIERE",
+    "LabelLiveProgram": "EN VIVO",
+    "LabelNewProgram": "NUEVO",
+    "LabelPremiereProgram": "ESTRENO",
     "LabelHDProgram": "HD",
-    "HeaderChangeFolderType": "Change Folder Type",
+    "HeaderChangeFolderType": "Cambiar tipo de carpeta",
     "HeaderChangeFolderTypeHelp": "To change the folder type, please remove and rebuild the collection with the new type.",
     "HeaderAlert": "Alert",
     "MessagePleaseRestart": "Please restart to finish updating.",

+ 22 - 22
MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json

@@ -346,33 +346,33 @@
     "HeaderSeasonNumber": "N\u00famero de temporada",
     "HeaderNetwork": "Cadena",
     "HeaderYear": "A\u00f1o",
-    "HeaderGameSystem": "Sistema de Juegos",
+    "HeaderGameSystem": "Sistema de juegos",
     "HeaderPlayers": "Jugadores",
     "HeaderEmbeddedImage": "Im\u00e1gen embebida",
     "HeaderTrack": "Pista",
     "HeaderDisc": "Disco",
     "OptionMovies": "Pel\u00edculas",
-    "OptionCollections": "Collection",
+    "OptionCollections": "Colecci\u00f3n",
     "OptionSeries": "Series",
-    "OptionSeasons": "Seasons",
+    "OptionSeasons": "Temporadas",
     "OptionEpisodes": "Episodios",
-    "OptionGames": "Games",
-    "OptionGameSystems": "Game systems",
-    "OptionMusicArtists": "Music artists",
-    "OptionMusicAlbums": "Music albums",
-    "OptionMusicVideos": "Music videos",
-    "OptionSongs": "Songs",
-    "OptionHomeVideos": "Home videos",
-    "OptionBooks": "Books",
-    "OptionAdultVideos": "Adult videos",
-    "ButtonUp": "Up",
-    "ButtonDown": "Down",
-    "LabelMetadataReaders": "Metadata readers:",
-    "LabelMetadataReadersHelp": "Rank your preferred local metadata sources in order of priority. The first file found will be read.",
-    "LabelMetadataDownloaders": "Metadata downloaders:",
-    "LabelMetadataDownloadersHelp": "Enable and rank your preferred metadata downloaders in order of priority. Lower priority downloaders will only be used to fill in missing information.",
-    "LabelMetadataSavers": "Metadata savers:",
-    "LabelMetadataSaversHelp": "Choose the file formats to save your metadata to.",
-    "LabelImageFetchers": "Image fetchers:",
-    "LabelImageFetchersHelp": "Enable and rank your preferred image fetchers in order of priority."
+    "OptionGames": "Juegos",
+    "OptionGameSystems": "Sistemas de juegos",
+    "OptionMusicArtists": "Int\u00e9rpretes",
+    "OptionMusicAlbums": "\u00c1lbums musicales",
+    "OptionMusicVideos": "Videos musicales",
+    "OptionSongs": "Canciones",
+    "OptionHomeVideos": "Videos caseros",
+    "OptionBooks": "Libros",
+    "OptionAdultVideos": "Videos para adultos",
+    "ButtonUp": "Arriba",
+    "ButtonDown": "Abajo",
+    "LabelMetadataReaders": "Lectores de metadatos:",
+    "LabelMetadataReadersHelp": "Ordene sus fuentes de metadatos locales por prioridad. El primer archivo encontrado ser\u00e1 le\u00eddo.",
+    "LabelMetadataDownloaders": "Recolectores de metadatos:",
+    "LabelMetadataDownloadersHelp": "Habilite y priorice sus recolectores de metadatos preferidos. Los recolectores de metadatos de menor prioridad solo ser\u00e1n utilizados para llenar informaci\u00f3n faltante.",
+    "LabelMetadataSavers": "Grabadores de metadatos:",
+    "LabelMetadataSaversHelp": "Seleccione los formatos de archivo con los que se guardaran sus metadatos.",
+    "LabelImageFetchers": "Recolectores de im\u00e1genes:",
+    "LabelImageFetchersHelp": "Habilite y priorice sus recolectores de im\u00e1genes preferidos."
 }

+ 6 - 6
MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json

@@ -222,7 +222,7 @@
     "HeaderBlockItemsWithNoRating": "Bloquer les items ne comportant aucune information de classement :",
     "OptionBlockOthers": "Autres",
     "OptionBlockTvShows": "Emissions TV",
-    "OptionBlockTrailers": "Bande-annonces",
+    "OptionBlockTrailers": "Bandes-annonces",
     "OptionBlockMusic": "Musiques",
     "OptionBlockMovies": "Films",
     "OptionBlockBooks": "Livres",
@@ -332,7 +332,7 @@
     "ValueOriginalAirDate": "Date de diffusion originale: {0}",
     "ButtonRemoveFromPlaylist": "Remove from playlist",
     "HeaderSpecials": "Specials",
-    "HeaderTrailers": "Bande-Annonces",
+    "HeaderTrailers": "Bandes-annonces",
     "HeaderAudio": "Audio",
     "HeaderResolution": "R\u00e9solution",
     "HeaderVideo": "Vid\u00e9o",
@@ -354,16 +354,16 @@
     "OptionMovies": "Films",
     "OptionCollections": "Collection",
     "OptionSeries": "Series",
-    "OptionSeasons": "Seasons",
+    "OptionSeasons": "Saisons",
     "OptionEpisodes": "\u00c9pisodes",
-    "OptionGames": "Games",
+    "OptionGames": "Jeux",
     "OptionGameSystems": "Game systems",
     "OptionMusicArtists": "Music artists",
     "OptionMusicAlbums": "Music albums",
     "OptionMusicVideos": "Music videos",
-    "OptionSongs": "Songs",
+    "OptionSongs": "Chansons",
     "OptionHomeVideos": "Home videos",
-    "OptionBooks": "Books",
+    "OptionBooks": "Livres",
     "OptionAdultVideos": "Adult videos",
     "ButtonUp": "Up",
     "ButtonDown": "Down",

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

@@ -358,7 +358,7 @@
     "HeaderTrack": "Track",
     "HeaderDisc": "Disc",
     "OptionMovies": "Movies",
-    "OptionCollections": "Collection",
+    "OptionCollections": "Collections",
     "OptionSeries": "Series",
     "OptionSeasons": "Seasons",
     "OptionEpisodes": "Episodes",

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

@@ -146,7 +146,7 @@
     "ButtonAdd": "\u04ae\u0441\u0442\u0435\u0443",
     "ButtonRemove": "\u0410\u043b\u0430\u0441\u0442\u0430\u0443",
     "LabelChapterDownloaders": "\u0421\u0430\u0445\u043d\u0430\u043b\u0430\u0440\u0434\u044b \u0436\u04af\u043a\u0442\u0435\u0443\u0448\u0456\u043b\u0435\u0440:",
-    "LabelChapterDownloadersHelp": "\u0421\u0430\u0445\u043d\u0430 \u0436\u04af\u043a\u0442\u0435\u0443\u0448\u0456\u043b\u0435\u0440\u0456\u043d \u049b\u043e\u0441\u044b\u04a3\u044b\u0437 \u0436\u04d9\u043d\u0435 \u0431\u0430\u0441\u044b\u043c\u0434\u044b\u043b\u044b\u049b \u0440\u0435\u0442\u0456 \u0431\u043e\u0439\u044b\u043d\u0448\u0430 \u0434\u04d9\u0440\u0435\u0436\u0435 \u0431\u0435\u0440\u0456\u04a3\u0456\u0437. \u0422\u04e9\u043c\u0435\u043d\u0433\u0456 \u0431\u0430\u0441\u044b\u043c\u0434\u044b\u043b\u044b\u0493\u044b \u0431\u0430\u0440 \u0436\u04af\u043a\u0442\u0435\u0443\u0448\u0456\u043b\u0435\u0440 \u0442\u0435\u043a \u049b\u0430\u043d\u0430 \u0436\u043e\u049b \u0430\u049b\u043f\u0430\u0440\u0430\u0442\u0442\u044b \u0442\u043e\u043b\u0442\u044b\u0440\u0443 \u04af\u0448\u0456\u043d \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u044b\u043b\u0430\u0434\u044b.",
+    "LabelChapterDownloadersHelp": "\u0422\u0435\u04a3\u0448\u0435\u043b\u0433\u0435\u043d \u0441\u0430\u0445\u043d\u0430 \u0436\u04af\u043a\u0442\u0435\u0443\u0448\u0456\u043b\u0435\u0440\u0456\u043d \u049b\u043e\u0441\u044b\u04a3\u044b\u0437 \u0436\u04d9\u043d\u0435 \u0431\u0430\u0441\u044b\u043c\u0434\u044b\u043b\u044b\u049b \u0440\u0435\u0442\u0456 \u0431\u043e\u0439\u044b\u043d\u0448\u0430 \u0434\u04d9\u0440\u0435\u0436\u0435 \u0431\u0435\u0440\u0456\u04a3\u0456\u0437. \u0422\u04e9\u043c\u0435\u043d\u0433\u0456 \u0431\u0430\u0441\u044b\u043c\u0434\u044b\u043b\u044b\u0493\u044b \u0431\u0430\u0440 \u0436\u04af\u043a\u0442\u0435\u0443\u0448\u0456\u043b\u0435\u0440 \u0442\u0435\u043a \u049b\u0430\u043d\u0430 \u0436\u043e\u049b \u0430\u049b\u043f\u0430\u0440\u0430\u0442\u0442\u044b \u0442\u043e\u043b\u0442\u044b\u0440\u0443 \u04af\u0448\u0456\u043d \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u044b\u043b\u0430\u0434\u044b.",
     "HeaderFavoriteAlbums": "\u0422\u0430\u04a3\u0434\u0430\u0443\u043b\u044b \u0430\u043b\u044c\u0431\u043e\u043c\u0434\u0430\u0440",
     "HeaderLatestChannelMedia": "\u0410\u0440\u043d\u0430\u043b\u0430\u0440\u0434\u044b\u04a3 \u0435\u04a3 \u043a\u0435\u0439\u0456\u043d\u0433\u0456 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0442\u0435\u0440\u0456",
     "ButtonOrganizeFile": "\u0424\u0430\u0439\u043b\u0434\u044b \u04b1\u0439\u044b\u043c\u0434\u0430\u0441\u0442\u044b\u0440\u0443",
@@ -352,27 +352,27 @@
     "HeaderTrack": "\u0416\u043e\u043b\u0448\u044b\u049b",
     "HeaderDisc": "\u0414\u0438\u0441\u043a\u0456",
     "OptionMovies": "\u0424\u0438\u043b\u044c\u043c\u0434\u0435\u0440",
-    "OptionCollections": "Collection",
-    "OptionSeries": "Series",
-    "OptionSeasons": "Seasons",
+    "OptionCollections": "\u0416\u0438\u043d\u0430\u049b\u0442\u0430\u0440",
+    "OptionSeries": "\u0421\u0435\u0440\u0438\u0430\u043b\u0434\u0430\u0440",
+    "OptionSeasons": "\u041c\u0430\u0443\u0441\u044b\u043c\u0434\u0430\u0440",
     "OptionEpisodes": "\u042d\u043f\u0438\u0437\u043e\u0434\u0442\u0430\u0440",
-    "OptionGames": "Games",
-    "OptionGameSystems": "Game systems",
-    "OptionMusicArtists": "Music artists",
-    "OptionMusicAlbums": "Music albums",
-    "OptionMusicVideos": "Music videos",
-    "OptionSongs": "Songs",
-    "OptionHomeVideos": "Home videos",
-    "OptionBooks": "Books",
-    "OptionAdultVideos": "Adult videos",
-    "ButtonUp": "Up",
-    "ButtonDown": "Down",
-    "LabelMetadataReaders": "Metadata readers:",
-    "LabelMetadataReadersHelp": "Rank your preferred local metadata sources in order of priority. The first file found will be read.",
-    "LabelMetadataDownloaders": "Metadata downloaders:",
-    "LabelMetadataDownloadersHelp": "Enable and rank your preferred metadata downloaders in order of priority. Lower priority downloaders will only be used to fill in missing information.",
-    "LabelMetadataSavers": "Metadata savers:",
-    "LabelMetadataSaversHelp": "Choose the file formats to save your metadata to.",
-    "LabelImageFetchers": "Image fetchers:",
-    "LabelImageFetchersHelp": "Enable and rank your preferred image fetchers in order of priority."
+    "OptionGames": "\u041e\u0439\u044b\u043d\u0434\u0430\u0440",
+    "OptionGameSystems": "\u041e\u0439\u044b\u043d \u0436\u04af\u0439\u0435\u043b\u0435\u0440\u0456",
+    "OptionMusicArtists": "\u041c\u0443\u0437\u044b\u043a\u0430 \u043e\u0440\u044b\u043d\u0434\u0430\u0443\u0448\u044b\u043b\u0430\u0440\u044b",
+    "OptionMusicAlbums": "\u041c\u0443\u0437\u044b\u043a\u0430 \u0430\u043b\u044c\u0431\u043e\u043c\u0434\u0430\u0440\u044b",
+    "OptionMusicVideos": "\u041c\u0443\u0437\u044b\u043a\u0430 \u043a\u043b\u0438\u043f\u0442\u0435\u0440\u0456",
+    "OptionSongs": "\u04d8\u0443\u0435\u043d\u0434\u0435\u0440",
+    "OptionHomeVideos": "\u04ae\u0439 \u0431\u0435\u0439\u043d\u0435\u043b\u0435\u0440\u0456",
+    "OptionBooks": "\u041a\u0456\u0442\u0430\u043f\u0442\u0430\u0440",
+    "OptionAdultVideos": "\u0415\u0440\u0435\u0441\u0435\u043a \u0431\u0435\u0439\u043d\u0435\u043b\u0435\u0440\u0456",
+    "ButtonUp": "\u0416\u043e\u0493\u0430\u0440\u044b\u0493\u0430",
+    "ButtonDown": "\u0422\u04e9\u043c\u0435\u043d\u0433\u0435",
+    "LabelMetadataReaders": "\u041c\u0435\u0442\u0430\u0434\u0435\u0440\u0435\u043a \u043e\u049b\u0443\u0448\u044b\u043b\u0430\u0440\u044b:",
+    "LabelMetadataReadersHelp": "\u0422\u0435\u04a3\u0448\u0435\u043b\u0433\u0435\u043d \u043c\u0435\u0442\u0430\u0434\u0435\u0440\u0435\u043a \u049b\u0430\u0439\u043d\u0430\u0440 \u043a\u04e9\u0437\u0434\u0435\u0440\u0456\u043d\u0435 \u0431\u0430\u0441\u044b\u043c\u0434\u044b\u043b\u044b\u049b \u0440\u0435\u0442\u0456 \u0431\u043e\u0439\u044b\u043d\u0448\u0430 \u0434\u04d9\u0440\u0435\u0436\u0435 \u0431\u0435\u0440\u0456\u04a3\u0456\u0437. \u0411\u0456\u0440\u0456\u043d\u0448\u0456 \u0442\u0430\u0431\u044b\u043b\u0493\u0430\u043d \u0444\u0430\u0439\u043b \u043e\u049b\u044b\u043b\u0430\u0434\u044b.",
+    "LabelMetadataDownloaders": "\u041c\u0435\u0442\u0430\u0434\u0435\u0440\u0435\u043a \u0436\u04af\u043a\u0442\u0435\u0443\u0448\u0456\u043b\u0435\u0440\u0456:",
+    "LabelMetadataDownloadersHelp": "\u0422\u0435\u04a3\u0448\u0435\u043b\u0433\u0435\u043d \u043c\u0435\u0442\u0430\u0434\u0435\u0440\u0435\u043a \u0436\u04af\u043a\u0442\u0435\u0443\u0448\u0456\u043b\u0435\u0440\u0456\u043d \u049b\u043e\u0441\u044b\u04a3\u044b\u0437 \u0436\u04d9\u043d\u0435 \u0431\u0430\u0441\u044b\u043c\u0434\u044b\u043b\u044b\u049b \u0440\u0435\u0442\u0456 \u0431\u043e\u0439\u044b\u043d\u0448\u0430 \u0434\u04d9\u0440\u0435\u0436\u0435 \u0431\u0435\u0440\u0456\u04a3\u0456\u0437. \u0422\u04e9\u043c\u0435\u043d\u0433\u0456 \u0431\u0430\u0441\u044b\u043c\u0434\u044b\u043b\u044b\u0493\u044b \u0431\u0430\u0440 \u0436\u04af\u043a\u0442\u0435\u0443\u0448\u0456\u043b\u0435\u0440 \u0442\u0435\u043a \u049b\u0430\u043d\u0430 \u0436\u043e\u049b \u0430\u049b\u043f\u0430\u0440\u0430\u0442\u0442\u044b \u0442\u043e\u043b\u0442\u044b\u0440\u0443 \u04af\u0448\u0456\u043d \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u044b\u043b\u0430\u0434\u044b.",
+    "LabelMetadataSavers": "\u041c\u0435\u0442\u0430\u0434\u0435\u0440\u0435\u043a \u0441\u0430\u049b\u0442\u0430\u0443\u0448\u044b\u043b\u0430\u0440\u044b:",
+    "LabelMetadataSaversHelp": "\u041c\u0435\u0442\u0430\u0434\u0435\u0440\u0435\u043a\u0442\u0435\u0440\u0434\u0456 \u049b\u0430\u0439\u0434\u0430 \u0441\u0430\u049b\u0442\u0430\u0439\u0442\u044b\u043d \u0444\u0430\u0439\u043b \u043f\u0456\u0448\u0456\u043c\u0434\u0435\u0440\u0456\u043d \u0442\u0430\u04a3\u0434\u0430\u0443.",
+    "LabelImageFetchers": "\u0421\u0443\u0440\u0435\u0442 \u0456\u0440\u0456\u043a\u0442\u0435\u0443\u0448\u0456\u043b\u0435\u0440\u0456:",
+    "LabelImageFetchersHelp": "\u0422\u0435\u04a3\u0448\u0435\u043b\u0433\u0435\u043d \u0441\u0443\u0440\u0435\u0442 \u0456\u0440\u0456\u043a\u0442\u0435\u0443\u0448\u0456\u043b\u0435\u0440\u0456\u043d \u049b\u043e\u0441\u044b\u04a3\u044b\u0437 \u0436\u04d9\u043d\u0435 \u0431\u0430\u0441\u044b\u043c\u0434\u044b\u043b\u044b\u049b \u0440\u0435\u0442\u0456 \u0431\u043e\u0439\u044b\u043d\u0448\u0430 \u0434\u04d9\u0440\u0435\u0436\u0435 \u0431\u0435\u0440\u0456\u04a3\u0456\u0437."
 }

+ 20 - 20
MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json

@@ -352,27 +352,27 @@
     "HeaderTrack": "Track",
     "HeaderDisc": "Schijf",
     "OptionMovies": "Films",
-    "OptionCollections": "Collection",
+    "OptionCollections": "Collectie",
     "OptionSeries": "Series",
-    "OptionSeasons": "Seasons",
+    "OptionSeasons": "Seizoenen",
     "OptionEpisodes": "Afleveringen",
-    "OptionGames": "Games",
-    "OptionGameSystems": "Game systems",
-    "OptionMusicArtists": "Music artists",
-    "OptionMusicAlbums": "Music albums",
+    "OptionGames": "Spellen",
+    "OptionGameSystems": "Spel Systemen",
+    "OptionMusicArtists": "Muziek artiesten",
+    "OptionMusicAlbums": "Muziek albums",
     "OptionMusicVideos": "Music videos",
-    "OptionSongs": "Songs",
-    "OptionHomeVideos": "Home videos",
-    "OptionBooks": "Books",
-    "OptionAdultVideos": "Adult videos",
-    "ButtonUp": "Up",
-    "ButtonDown": "Down",
-    "LabelMetadataReaders": "Metadata readers:",
-    "LabelMetadataReadersHelp": "Rank your preferred local metadata sources in order of priority. The first file found will be read.",
-    "LabelMetadataDownloaders": "Metadata downloaders:",
-    "LabelMetadataDownloadersHelp": "Enable and rank your preferred metadata downloaders in order of priority. Lower priority downloaders will only be used to fill in missing information.",
-    "LabelMetadataSavers": "Metadata savers:",
-    "LabelMetadataSaversHelp": "Choose the file formats to save your metadata to.",
-    "LabelImageFetchers": "Image fetchers:",
-    "LabelImageFetchersHelp": "Enable and rank your preferred image fetchers in order of priority."
+    "OptionSongs": "Nummers ",
+    "OptionHomeVideos": "Home video's",
+    "OptionBooks": "Boeken",
+    "OptionAdultVideos": "Erotische video's",
+    "ButtonUp": "Omhoog",
+    "ButtonDown": "Omlaag",
+    "LabelMetadataReaders": "Metagedata lezers:",
+    "LabelMetadataReadersHelp": "Rangschik de gewenste lokale metadata bronnen in volgorde van prioriteit. Het eerst gevonden bestand zal worden gelezen.",
+    "LabelMetadataDownloaders": "Metadata Downloaders:",
+    "LabelMetadataDownloadersHelp": "Schakelen in en rangschik uw voorkeurs metadata downloader, gerangschikt in volgorde van prioriteit. Lagere prioriteit downloaders zullen alleen worden gebruikt om de ontbrekende informatie in te vullen.",
+    "LabelMetadataSavers": "Metadata Opslag:",
+    "LabelMetadataSaversHelp": "Kies de bestandsindeling om uw metadata op te slaan.",
+    "LabelImageFetchers": "Afbeeldingen Downloaders:",
+    "LabelImageFetchersHelp": "Schakelen in en rangschik uw voorkeurs Afbeeldingen downloader, gerangschikt in volgorde van prioriteit."
 }

+ 26 - 26
MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json

@@ -58,14 +58,14 @@
     "ButtonMute": "Mudo",
     "ButtonUnmute": "Remover Mudo",
     "ButtonStop": "Parar",
-    "ButtonNextTrack": "Pr\u00f3xima faixa",
+    "ButtonNextTrack": "Pr\u00f3xima Faixa",
     "ButtonPause": "Pausar",
     "ButtonPlay": "Reproduzir",
     "ButtonEdit": "Editar",
     "ButtonQueue": "Adicionar \u00e0 fila",
     "ButtonPlayTrailer": "Reproduzir trailer",
     "ButtonPlaylist": "Lista de reprodu\u00e7\u00e3o",
-    "ButtonPreviousTrack": "Faixa anterior",
+    "ButtonPreviousTrack": "Faixa Anterior",
     "LabelEnabled": "Ativada",
     "LabelDisabled": "Desativada",
     "ButtonMoreInformation": "Mais informa\u00e7\u00f5es",
@@ -153,7 +153,7 @@
     "ButtonDeleteFile": "Excluir Arquivo",
     "HeaderOrganizeFile": "Organizar Arquivo",
     "HeaderDeleteFile": "Excluir Arquivo",
-    "StatusSkipped": "Ignorado",
+    "StatusSkipped": "Ignorada",
     "StatusFailed": "Com Falha",
     "StatusSuccess": "Sucesso",
     "MessageFileWillBeDeleted": "Ser\u00e1 exclu\u00eddo o seguinte arquivo:",
@@ -326,7 +326,7 @@
     "LabelName": "Nome:",
     "ButtonSubmit": "Enviar",
     "LabelSelectPlaylist": "Lista de Reprodu\u00e7\u00e3o:",
-    "OptionNewPlaylist": "Nova lista de reprodu\u00e7\u00e3o...",
+    "OptionNewPlaylist": "Nova lista de reprodu\u00e7\u00e3o",
     "MessageAddedToPlaylistSuccess": "Ok",
     "ButtonViewSeriesRecording": "Visualizar grava\u00e7\u00e3o de s\u00e9ries",
     "ValueOriginalAirDate": "Data original de exibi\u00e7\u00e3o: {0}",
@@ -352,27 +352,27 @@
     "HeaderTrack": "Faixa",
     "HeaderDisc": "Disco",
     "OptionMovies": "Filmes",
-    "OptionCollections": "Collection",
-    "OptionSeries": "Series",
-    "OptionSeasons": "Seasons",
+    "OptionCollections": "Cole\u00e7\u00e3o",
+    "OptionSeries": "S\u00e9ries",
+    "OptionSeasons": "Temporadas",
     "OptionEpisodes": "Epis\u00f3dios",
-    "OptionGames": "Games",
-    "OptionGameSystems": "Game systems",
-    "OptionMusicArtists": "Music artists",
-    "OptionMusicAlbums": "Music albums",
-    "OptionMusicVideos": "Music videos",
-    "OptionSongs": "Songs",
-    "OptionHomeVideos": "Home videos",
-    "OptionBooks": "Books",
-    "OptionAdultVideos": "Adult videos",
-    "ButtonUp": "Up",
-    "ButtonDown": "Down",
-    "LabelMetadataReaders": "Metadata readers:",
-    "LabelMetadataReadersHelp": "Rank your preferred local metadata sources in order of priority. The first file found will be read.",
-    "LabelMetadataDownloaders": "Metadata downloaders:",
-    "LabelMetadataDownloadersHelp": "Enable and rank your preferred metadata downloaders in order of priority. Lower priority downloaders will only be used to fill in missing information.",
-    "LabelMetadataSavers": "Metadata savers:",
-    "LabelMetadataSaversHelp": "Choose the file formats to save your metadata to.",
-    "LabelImageFetchers": "Image fetchers:",
-    "LabelImageFetchersHelp": "Enable and rank your preferred image fetchers in order of priority."
+    "OptionGames": "Jogos",
+    "OptionGameSystems": "Sistemas de jogos",
+    "OptionMusicArtists": "Artistas da M\u00fasica",
+    "OptionMusicAlbums": "\u00c1lbuns de m\u00fasica",
+    "OptionMusicVideos": "V\u00eddeos Musicais",
+    "OptionSongs": "M\u00fasicas",
+    "OptionHomeVideos": "V\u00eddeos caseiros",
+    "OptionBooks": "Livros",
+    "OptionAdultVideos": "V\u00eddeos adultos",
+    "ButtonUp": "Para cima",
+    "ButtonDown": "Para baixo",
+    "LabelMetadataReaders": "Leitores de metadados:",
+    "LabelMetadataReadersHelp": "Classifique suas fontes preferidas de metadados locais por ordem de prioridade. O primeiro arquivo encontrado ser\u00e1 lido.",
+    "LabelMetadataDownloaders": "Downloaders de metadados:",
+    "LabelMetadataDownloadersHelp": "Ative e classifique seus downloaders preferidos de metadados por ordem de prioridade. Downloaders com prioridade mais baixa s\u00f3 ser\u00e3o usados para baixar informa\u00e7\u00e3o que n\u00e3o existe.",
+    "LabelMetadataSavers": "Gravadores de metadados:",
+    "LabelMetadataSaversHelp": "Escolha os formatos de arquivos nos quais deseja gravar seus metadados.",
+    "LabelImageFetchers": "Buscadores de imagem:",
+    "LabelImageFetchersHelp": "Ative e classifique seus buscadores preferidos de imagem por ordem de prioridade."
 }

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

@@ -146,7 +146,7 @@
     "ButtonAdd": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c",
     "ButtonRemove": "\u0418\u0437\u044a\u044f\u0442\u044c",
     "LabelChapterDownloaders": "\u0417\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a\u0438 \u0441\u0446\u0435\u043d:",
-    "LabelChapterDownloadersHelp": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u0438 \u0440\u0430\u043d\u0436\u0438\u0440\u0443\u0439\u0442\u0435 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u0435\u043c\u044b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a\u0438 \u0441\u0446\u0435\u043d \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u0430. \u0417\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a\u0438 \u043d\u0438\u0437\u043a\u043e\u0433\u043e \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u0430 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0441\u0432\u0435\u0434\u0435\u043d\u0438\u0439.",
+    "LabelChapterDownloadersHelp": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u0438 \u0440\u0430\u043d\u0436\u0438\u0440\u0443\u0439\u0442\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a\u0438 \u0441\u0446\u0435\u043d \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u0430. \u0417\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a\u0438 \u043d\u0438\u0437\u043a\u043e\u0433\u043e \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u0430 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u044e\u0449\u0435\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.",
     "HeaderFavoriteAlbums": "\u0418\u0437\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0430\u043b\u044c\u0431\u043e\u043c\u044b",
     "HeaderLatestChannelMedia": "\u041d\u043e\u0432\u0438\u043d\u043a\u0438 \u043a\u0430\u043d\u0430\u043b\u043e\u0432",
     "ButtonOrganizeFile": "\u0420\u0435\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b",
@@ -352,27 +352,27 @@
     "HeaderTrack": "\u0414\u043e\u0440\u043e\u0436\u043a\u0430",
     "HeaderDisc": "\u0414\u0438\u0441\u043a",
     "OptionMovies": "\u0424\u0438\u043b\u044c\u043c\u044b",
-    "OptionCollections": "Collection",
-    "OptionSeries": "Series",
-    "OptionSeasons": "Seasons",
+    "OptionCollections": "\u041a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438",
+    "OptionSeries": "\u0421\u0435\u0440\u0438\u0430\u043b\u044b",
+    "OptionSeasons": "\u0421\u0435\u0437\u043e\u043d\u044b",
     "OptionEpisodes": "\u042d\u043f\u0438\u0437\u043e\u0434\u044b",
-    "OptionGames": "Games",
-    "OptionGameSystems": "Game systems",
-    "OptionMusicArtists": "Music artists",
-    "OptionMusicAlbums": "Music albums",
-    "OptionMusicVideos": "Music videos",
-    "OptionSongs": "Songs",
-    "OptionHomeVideos": "Home videos",
-    "OptionBooks": "Books",
-    "OptionAdultVideos": "Adult videos",
-    "ButtonUp": "Up",
-    "ButtonDown": "Down",
-    "LabelMetadataReaders": "Metadata readers:",
-    "LabelMetadataReadersHelp": "Rank your preferred local metadata sources in order of priority. The first file found will be read.",
-    "LabelMetadataDownloaders": "Metadata downloaders:",
-    "LabelMetadataDownloadersHelp": "Enable and rank your preferred metadata downloaders in order of priority. Lower priority downloaders will only be used to fill in missing information.",
-    "LabelMetadataSavers": "Metadata savers:",
-    "LabelMetadataSaversHelp": "Choose the file formats to save your metadata to.",
-    "LabelImageFetchers": "Image fetchers:",
-    "LabelImageFetchersHelp": "Enable and rank your preferred image fetchers in order of priority."
+    "OptionGames": "\u0418\u0433\u0440\u044b",
+    "OptionGameSystems": "\u0418\u0433\u0440\u043e\u0432\u044b\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b",
+    "OptionMusicArtists": "\u041c\u0443\u0437\u044b\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0438\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u0438",
+    "OptionMusicAlbums": "\u041c\u0443\u0437\u044b\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u043b\u044c\u0431\u043e\u043c\u044b",
+    "OptionMusicVideos": "\u041c\u0443\u0437\u044b\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u043a\u043b\u0438\u043f\u044b",
+    "OptionSongs": "\u041c\u0435\u043b\u043e\u0434\u0438\u0438",
+    "OptionHomeVideos": "\u0414\u043e\u043c\u0430\u0448\u043d\u0435\u0435 \u0432\u0438\u0434\u0435\u043e",
+    "OptionBooks": "\u041a\u043d\u0438\u0433\u0438",
+    "OptionAdultVideos": "\u0412\u0437\u0440\u043e\u0441\u043b\u043e\u0435 \u0432\u0438\u0434\u0435\u043e",
+    "ButtonUp": "\u0412\u0432\u0435\u0440\u0445",
+    "ButtonDown": "\u0412\u043d\u0438\u0437",
+    "LabelMetadataReaders": "\u0421\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u0435\u043b\u0438 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445:",
+    "LabelMetadataReadersHelp": "\u0420\u0430\u043d\u0436\u0438\u0440\u0443\u0439\u0442\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u0430. \u0411\u0443\u0434\u0435\u0442 \u0441\u0447\u0438\u0442\u0430\u043d \u043f\u0435\u0440\u0432\u044b\u0439 \u0436\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b.",
+    "LabelMetadataDownloaders": "\u0417\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a\u0438 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445:",
+    "LabelMetadataDownloadersHelp": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u0438 \u0440\u0430\u043d\u0436\u0438\u0440\u0443\u0439\u0442\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a\u0438 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u0430. \u0417\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a\u0438 \u0441 \u043d\u0438\u0437\u043a\u0438\u043c \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u044e\u0449\u0435\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.",
+    "LabelMetadataSavers": "\u0425\u0440\u0430\u043d\u0438\u0442\u0435\u043b\u0438 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445:",
+    "LabelMetadataSaversHelp": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0444\u043e\u0440\u043c\u0430\u0442\u044b \u0444\u0430\u0439\u043b\u043e\u0432, \u043a\u0443\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c\u0441\u044f \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435.",
+    "LabelImageFetchers": "\u041e\u0442\u0431\u043e\u0440\u0449\u0438\u043a\u0438 \u0440\u0438\u0441\u0443\u043d\u043a\u043e\u0432:",
+    "LabelImageFetchersHelp": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u0438 \u0440\u0430\u043d\u0436\u0438\u0440\u0443\u0439\u0442\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u043e\u0442\u0431\u043e\u0440\u0449\u0438\u043a\u0438 \u0440\u0438\u0441\u0443\u043d\u043a\u043e\u0432 \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u0430."
 }

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

@@ -321,12 +321,12 @@
     "ButtonNew": "Nytillkommet",
     "MessageInternetExplorerWebm": "F\u00f6r b\u00e4sta resultat med Internet Explorer, installera WebM-till\u00e4gget f\u00f6r IE.",
     "HeaderVideoError": "Videofel",
-    "ButtonAddToPlaylist": "Add to playlist",
-    "HeaderAddToPlaylist": "Add to Playlist",
+    "ButtonAddToPlaylist": "L\u00e4gg till i spellista",
+    "HeaderAddToPlaylist": "L\u00e4gg till i Spellista",
     "LabelName": "Namn:",
     "ButtonSubmit": "Bekr\u00e4fta",
-    "LabelSelectPlaylist": "Playlist:",
-    "OptionNewPlaylist": "New playlist...",
+    "LabelSelectPlaylist": "Spellista:",
+    "OptionNewPlaylist": "Ny spellista...",
     "MessageAddedToPlaylistSuccess": "Ok",
     "ButtonViewSeriesRecording": "View series recording",
     "ValueOriginalAirDate": "Original air date: {0}",

+ 5 - 5
MediaBrowser.Server.Implementations/Localization/Server/es.json

@@ -877,11 +877,11 @@
     "HeaderDevice": "Dispositivo",
     "HeaderUser": "Usuario",
     "HeaderDateIssued": "Fecha de emisi\u00f3n",
-    "LabelChapterName": "Chapter {0}",
-    "HeaderNewApiKey": "New Api Key",
-    "LabelAppName": "App name",
-    "LabelAppNameExample": "Example: Sickbeard, NzbDrone",
-    "HeaderNewApiKeyHelp": "Grant an application permission to communicate with Media Browser.",
+    "LabelChapterName": "Cap\u00edtulo {0}",
+    "HeaderNewApiKey": "Nueva Clave Api",
+    "LabelAppName": "Nombre de la app",
+    "LabelAppNameExample": "Ejemplo: Sickbeard, NzbDrone",
+    "HeaderNewApiKeyHelp": "Otorgar un permiso a la aplicaci\u00f3n para comunicarse con Media Browser.",
     "HeaderHttpHeaders": "Http Headers",
     "HeaderIdentificationHeader": "Identification Header",
     "LabelValue": "Value:",

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

@@ -374,7 +374,7 @@
     "HeaderGallery": "Galer\u00eda",
     "HeaderLatestGames": "Juegos Recientes",
     "HeaderRecentlyPlayedGames": "Juegos Usados Recientemente",
-    "TabGameSystems": "Sistemas de Juego",
+    "TabGameSystems": "Sistemas de Juegos",
     "TitleMediaLibrary": "Biblioteca de Medios",
     "TabFolders": "Carpetas",
     "TabPathSubstitution": "Rutas Alternativas",

+ 3 - 3
MediaBrowser.Server.Implementations/Localization/Server/fr.json

@@ -1,6 +1,6 @@
 {
     "LabelExit": "Quitter",
-    "HeaderPassword": "Password",
+    "HeaderPassword": "Mot de passe",
     "LabelVisitCommunity": "Visiter la Communaut\u00e9",
     "HeaderLocalAccess": "Local Access",
     "LabelGithubWiki": "GitHub Wiki",
@@ -180,7 +180,7 @@
     "OptionHasThemeVideo": "Vid\u00e9o th\u00e8me",
     "TabMovies": "Films",
     "TabStudios": "Studios",
-    "TabTrailers": "Bande-annonces",
+    "TabTrailers": "Bandes-annonces",
     "HeaderLatestMovies": "Films les plus r\u00e9cents",
     "HeaderLatestTrailers": "Bande-annonces les plus r\u00e9centes",
     "OptionHasSpecialFeatures": "Bonus:",
@@ -980,7 +980,7 @@
     "UserDeletedWithName": "L'utilisateur {0} a \u00e9t\u00e9 supprim\u00e9.",
     "MessageServerConfigurationUpdated": "Server configuration has been updated",
     "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
-    "MessageApplicationUpdated": "Media Browser Server has been updated",
+    "MessageApplicationUpdated": "Media Browser Server a \u00e9t\u00e9 mise \u00e0 jour.",
     "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
     "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
     "UserStartedPlayingItemWithValues": "{0} has started playing {1}",

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

@@ -349,7 +349,7 @@
     "OptionDownloadBackImage": "\u0410\u0440\u0442\u049b\u044b \u043c\u04b1\u049b\u0430\u0431\u0430",
     "OptionDownloadArtImage": "\u041e\u044e \u0441\u0443\u0440\u0435\u0442",
     "OptionDownloadPrimaryImage": "\u0411\u0430\u0441\u0442\u0430\u043f\u049b\u044b",
-    "HeaderFetchImages": "\u0421\u0443\u0440\u0435\u0442\u0442\u0435\u0440\u0434\u0456 \u0448\u044b\u0493\u0430\u0440\u044b\u043f \u0430\u043b\u0443:",
+    "HeaderFetchImages": "\u0421\u0443\u0440\u0435\u0442\u0442\u0435\u0440\u0434\u0456 \u0456\u0440\u0456\u043a\u0442\u0435\u0443:",
     "HeaderImageSettings": "\u0421\u0443\u0440\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043b\u0435\u0440\u0456",
     "TabOther": "\u0411\u0430\u0441\u049b\u0430\u043b\u0430\u0440",
     "LabelMaxBackdropsPerItem": "\u042d\u043b\u0435\u043c\u0435\u043d\u0442 \u0431\u043e\u0439\u044b\u043d\u0448\u0430 \u0430\u0440\u0442\u049b\u044b \u0441\u0443\u0440\u0435\u0442\u0442\u0435\u0440\u0434\u0456\u04a3 \u0435\u04a3 \u043a\u04e9\u043f \u0441\u0430\u043d\u044b:",
@@ -829,7 +829,7 @@
     "HeaderMyViews": "\u041c\u0435\u043d\u0456\u04a3 \u043a\u04e9\u0440\u0456\u043d\u0456\u0441\u0442\u0435\u0440\u0456\u043c",
     "LabelSelectFolderGroups": "\u041a\u0435\u043b\u0435\u0441\u0456 \u049b\u0430\u043b\u0442\u0430\u043b\u0430\u0440\u0434\u0430\u0493\u044b \u043c\u0430\u0437\u043c\u04b1\u043d\u0434\u044b \u041a\u0438\u043d\u043e, \u041c\u0443\u0437\u044b\u043a\u0430 \u0436\u04d9\u043d\u0435 \u0422\u0414 \u0441\u0438\u044f\u049b\u0442\u044b \u043a\u04e9\u0440\u0456\u043d\u0456\u0441\u0442\u0435\u0440\u0433\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0442\u044b \u0442\u04af\u0440\u0434\u0435 \u0442\u043e\u043f\u0442\u0430\u0441\u0442\u044b\u0440\u0443:",
     "LabelSelectFolderGroupsHelp": "\u0411\u0435\u043b\u0433\u0456\u043b\u0435\u043d\u0431\u0435\u0433\u0435\u043d \u049b\u0430\u043b\u0442\u0430\u043b\u0430\u0440 \u04e9\u0437 \u0431\u0435\u0442\u0456\u043c\u0435\u043d \u04e9\u0437\u0456\u043d\u0456\u04a3 \u043a\u04e9\u0440\u0456\u043d\u0456\u0441\u0456\u043d\u0434\u0435 \u0431\u0435\u0439\u043d\u0435\u043b\u0435\u043d\u0435\u0434\u0456.",
-    "OptionDisplayAdultContent": "\u0415\u0440\u0435\u0441\u0435\u043a\u0442\u0456\u043a \u043c\u0430\u0437\u043c\u04b1\u043d\u0434\u044b \u043a\u04e9\u0440\u0441\u0435\u0442\u0443",
+    "OptionDisplayAdultContent": "\u0415\u0440\u0435\u0441\u0435\u043a \u043c\u0430\u0437\u043c\u04b1\u043d\u044b\u043d \u043a\u04e9\u0440\u0441\u0435\u0442\u0443",
     "OptionLibraryFolders": "\u0422\u0430\u0441\u0443\u0448\u044b \u049b\u0430\u043b\u0442\u0430\u043b\u0430\u0440\u044b",
     "TitleRemoteControl": "\u049a\u0430\u0448\u044b\u049b\u0442\u0430\u043d \u0431\u0430\u0441\u049b\u0430\u0440\u0443",
     "OptionLatestTvRecordings": "\u0415\u04a3 \u043a\u0435\u0439\u0456\u043d\u0433\u0456 \u0436\u0430\u0437\u0431\u0430\u043b\u0430\u0440",
@@ -946,14 +946,14 @@
     "OptionReportTrailers": "\u0422\u0440\u0435\u0439\u043b\u0435\u0440\u043b\u0435\u0440",
     "OptionReportMusicVideos": "\u041a\u043b\u0438\u043f\u0442\u0435\u0440",
     "OptionReportMovies": "\u0424\u0438\u043b\u044c\u043c\u0434\u0435\u0440",
-    "OptionReportHomeVideos": "\u04ae\u0439\u043b\u0456\u043a \u0431\u0435\u0439\u043d\u0435\u043b\u0435\u0440",
+    "OptionReportHomeVideos": "\u04ae\u0439 \u0431\u0435\u0439\u043d\u0435\u043b\u0435\u0440\u0456",
     "OptionReportGames": "\u041e\u0439\u044b\u043d\u0434\u0430\u0440",
     "OptionReportEpisodes": "\u042d\u043f\u0438\u0437\u043e\u0434\u0442\u0430\u0440",
     "OptionReportCollections": "\u0416\u0438\u043d\u0430\u049b\u0442\u0430\u0440",
     "OptionReportBooks": "\u041a\u0456\u0442\u0430\u043f\u0442\u0430\u0440",
     "OptionReportArtists": "\u041e\u0440\u044b\u043d\u0434\u0430\u0443\u0448\u044b\u043b\u0430\u0440",
     "OptionReportAlbums": "\u0410\u043b\u044c\u0431\u043e\u043c\u0434\u0430\u0440",
-    "OptionReportAdultVideos": "\u0415\u0440\u0435\u0441\u0435\u043a\u0442\u0456\u043a \u0431\u0435\u0439\u043d\u0435\u043b\u0435\u0440",
+    "OptionReportAdultVideos": "\u0415\u0440\u0435\u0441\u0435\u043a \u0431\u0435\u0439\u043d\u0435\u043b\u0435\u0440\u0456",
     "ButtonMore": "\u041a\u04e9\u0431\u0456\u0440\u0435\u043a",
     "HeaderActivity": "\u04d8\u0440\u0435\u043a\u0435\u0442\u0442\u0435\u0440",
     "ScheduledTaskStartedWithName": "{0} \u0456\u0441\u043a\u0435 \u049b\u043e\u0441\u044b\u043b\u0434\u044b",

+ 13 - 13
MediaBrowser.Server.Implementations/Localization/Server/nl.json

@@ -45,10 +45,10 @@
     "ReferToMediaLibraryWiki": "Raadpleeg de mediabibliotheek wiki.",
     "LabelCountry": "Land:",
     "LabelLanguage": "Taal:",
-    "HeaderPreferredMetadataLanguage": "Gewenste metagegevens taal:",
-    "LabelSaveLocalMetadata": "Sla afbeeldingen en metagegevens op in de mediamappen",
-    "LabelSaveLocalMetadataHelp": "Door afbeeldingen en metagegevens op te slaan in de mediamappen kunnen ze makkelijker worden gevonden en bewerkt.",
-    "LabelDownloadInternetMetadata": "Download afbeeldingen en metagegevens van het internet",
+    "HeaderPreferredMetadataLanguage": "Gewenste metadata taal:",
+    "LabelSaveLocalMetadata": "Sla afbeeldingen en metadata op in de mediamappen",
+    "LabelSaveLocalMetadataHelp": "Door afbeeldingen en metadata op te slaan in de mediamappen kunnen ze makkelijker worden gevonden en bewerkt.",
+    "LabelDownloadInternetMetadata": "Download afbeeldingen en metadata van het internet",
     "LabelDownloadInternetMetadataHelp": "Media Browser kan informatie en afbeeldingen van uw media downloaden, om zo een mooie en uitgebreide weergave mogelijk te maken.",
     "TabPreferences": "Voorkeuren",
     "TabPassword": "Wachtwoord",
@@ -84,8 +84,8 @@
     "LabelCurrentPassword": "Huidig wachtwoord",
     "LabelMaxParentalRating": "Leeftijdsgrens",
     "MaxParentalRatingHelp": "Media met een hogere classificatie wordt niet weergegeven",
-    "LibraryAccessHelp": "Selecteer de mediamappen om met deze gebruiker te delen. Beheerders kunnen alle mappen bewerken via de metagegevens manager.",
-    "ChannelAccessHelp": "Selecteer de kanalen om te delen met deze gebruiker. Beheerders kunnen alle kanalen bewerken met de metagegevens manager.",
+    "LibraryAccessHelp": "Selecteer de mediamappen om met deze gebruiker te delen. Beheerders kunnen alle mappen bewerken via de metadata manager.",
+    "ChannelAccessHelp": "Selecteer de kanalen om te delen met deze gebruiker. Beheerders kunnen alle kanalen bewerken met de metadata manager.",
     "ButtonDeleteImage": "Verwijder afbeelding",
     "LabelSelectUsers": "Selecteer gebruikers:",
     "ButtonUpload": "Uploaden",
@@ -93,7 +93,7 @@
     "LabelDropImageHere": "Hier afbeelding plaatsen",
     "ImageUploadAspectRatioHelp": "1:1 beeldverhouding geadviseerd. Alleen JPG\/PNG.",
     "MessageNothingHere": "Lijst is leeg.",
-    "MessagePleaseEnsureInternetMetadata": "Zorg ervoor dat het downloaden van metagegevens van het internet is ingeschakeld.",
+    "MessagePleaseEnsureInternetMetadata": "Zorg ervoor dat het downloaden van metadata van het internet is ingeschakeld.",
     "TabSuggested": "Aanbevolen",
     "TabLatest": "Nieuw",
     "TabUpcoming": "Binnenkort",
@@ -205,7 +205,7 @@
     "OptionMissingImdbId": "IMDb Id ontbreekt",
     "OptionMissingTvdbId": "TheTVDB Id ontbreekt",
     "OptionMissingOverview": "Overzicht ontbreekt",
-    "OptionFileMetadataYearMismatch": "Jaartal in Bestands\/metagegevens komt niet overeen",
+    "OptionFileMetadataYearMismatch": "Jaartal in Bestands\/metadata komt niet overeen",
     "TabGeneral": "Algemeen",
     "TitleSupport": "Ondersteuning",
     "TabLog": "Logboek",
@@ -262,8 +262,8 @@
     "LabelCachePathHelp": "Deze locatie bevat server cache-bestanden, zoals afbeeldingen.",
     "LabelImagesByNamePath": "Afbeeldingen op naam pad:",
     "LabelImagesByNamePathHelp": "Deze locatie bevat afbeeldingen van: acteurs, artiesten, genres en studio's.",
-    "LabelMetadataPath": "metagegevens pad:",
-    "LabelMetadataPathHelp": "Geef een aangepaste locatie op voor gedownloade afbeeldingen en metagegevens, indien niet opgeslagen in mediamappen.",
+    "LabelMetadataPath": "metadata pad:",
+    "LabelMetadataPathHelp": "Geef een aangepaste locatie op voor gedownloade afbeeldingen en metadata, indien niet opgeslagen in mediamappen.",
     "LabelTranscodingTempPath": "Tijdelijk Transcodeer pad:",
     "LabelTranscodingTempPathHelp": "Deze map bevat werkbestanden die worden gebruikt door de transcoder. Geef een eigen locatie op of laat leeg om de standaardlocatie te gebruiken.",
     "TabBasics": "Basis",
@@ -275,7 +275,7 @@
     "OptionMovies": "Films",
     "OptionEpisodes": "Afleveringen",
     "OptionOtherVideos": "Overige Video's",
-    "TitleMetadata": "Metagegevens",
+    "TitleMetadata": "Metadata",
     "LabelAutomaticUpdatesFanart": "Schakel de automatische update in van FanArt.tv",
     "LabelAutomaticUpdatesTmdb": "Schakel de automatische update in van TheMovieDB.org",
     "LabelAutomaticUpdatesTvdb": "Schakel de automatische update in van TheTVDB.com",
@@ -457,7 +457,7 @@
     "HeaderAddToCollection": "Toevoegen aan verzameling",
     "ButtonSubmit": "Uitvoeren",
     "NewCollectionNameExample": "Voorbeeld: Star Wars Collectie",
-    "OptionSearchForInternetMetadata": "Zoeken op het internet voor afbeeldingen en metagegevens",
+    "OptionSearchForInternetMetadata": "Zoeken op het internet voor afbeeldingen en metadata",
     "ButtonCreate": "Cre\u00ebren",
     "LabelHttpServerPortNumber": "Http server poortnummer:",
     "LabelWebSocketPortNumber": "Web socket poortnummer:",
@@ -865,7 +865,7 @@
     "TitleServer": "Server",
     "LabelCache": "Cache:",
     "LabelLogs": "Logboeken:",
-    "LabelMetadata": "Metagegevens:",
+    "LabelMetadata": "Metadata:",
     "LabelImagesByName": "Afbeeldingen op naam:",
     "LabelTranscodingTemporaryFiles": "Tijdelijke transcodeer bestanden:",
     "HeaderLatestMusic": "Nieuwste muziek",

+ 11 - 11
MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json

@@ -1,8 +1,8 @@
 {
     "LabelExit": "Sair",
-    "HeaderPassword": "Password",
+    "HeaderPassword": "Senha",
     "LabelVisitCommunity": "Visitar a Comunidade",
-    "HeaderLocalAccess": "Local Access",
+    "HeaderLocalAccess": "Acesso Local",
     "LabelGithubWiki": "Wiki do Github",
     "LabelSwagger": "Swagger",
     "LabelStandard": "Padr\u00e3o",
@@ -481,9 +481,9 @@
     "HeaderDestination": "Destino",
     "HeaderProgram": "Programa",
     "HeaderClients": "Clientes",
-    "LabelCompleted": "Completado",
+    "LabelCompleted": "Completa",
     "LabelFailed": "Falhou",
-    "LabelSkipped": "Ignorado",
+    "LabelSkipped": "Ignorada",
     "HeaderEpisodeOrganization": "Organiza\u00e7\u00e3o do Epis\u00f3dio",
     "LabelSeries": "S\u00e9rie:",
     "LabelSeasonNumber": "N\u00famero da temporada",
@@ -994,11 +994,11 @@
     "HeaderDownloadPeopleMetadataFor": "Fazer download da biografia e imagens para:",
     "OptionComposers": "Compositores",
     "OptionOthers": "Outros",
-    "HeaderDownloadPeopleMetadataForHelp": "Enabling additional options will provide more on-screen information but will result in slower library scans.",
-    "ViewTypeFolders": "Folders",
-    "LabelDisplayFoldersView": "Display a folders view to show plain media folders",
-    "ViewTypeLiveTvRecordingGroups": "Recordings",
-    "ViewTypeLiveTvChannels": "Channels",
-    "LabelAllowLocalAccessWithoutPassword": "Allow local access without a password",
-    "LabelAllowLocalAccessWithoutPasswordHelp": "When enabled, a password will not be required when signing in from within your home network."
+    "HeaderDownloadPeopleMetadataForHelp": "Ativar op\u00e7\u00f5es adicionais disponibilizar\u00e1 mais informa\u00e7\u00f5es na tela mas deixar\u00e1 os rastreamentos de biblioteca mais lentos.",
+    "ViewTypeFolders": "Pastas",
+    "LabelDisplayFoldersView": "Exibir visualiza\u00e7\u00e3o de pastas para mostrar pastas simples de m\u00eddia",
+    "ViewTypeLiveTvRecordingGroups": "Grava\u00e7\u00f5es",
+    "ViewTypeLiveTvChannels": "Canais",
+    "LabelAllowLocalAccessWithoutPassword": "Permtir acesso local sem senha",
+    "LabelAllowLocalAccessWithoutPasswordHelp": "Quando ativado, uma senha n\u00e3o ser\u00e1 necess\u00e1ria para entrar atrav\u00e9s de sua rede dom\u00e9stica."
 }

文件差异内容过多而无法显示
+ 2 - 2
MediaBrowser.Server.Implementations/Localization/Server/ru.json


+ 1 - 1
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -870,7 +870,7 @@
     "HeaderBrandingHelp": "Customize the appearance of Media Browser to fit the needs of your group or organization.",
     "LabelLoginDisclaimer": "Login disclaimer:",
     "LabelLoginDisclaimerHelp": "This will be displayed at the bottom of the login page.",
-    "LabelAutomaticallyDonate": "Automatically donate this amount every six months",
+    "LabelAutomaticallyDonate": "Automatically donate this amount every month",
     "LabelAutomaticallyDonateHelp": "You can cancel at any time via your PayPal account.",
     "OptionList": "List",
     "TabDashboard": "Dashboard",

+ 15 - 15
MediaBrowser.Server.Implementations/Localization/Server/sv.json

@@ -482,10 +482,10 @@
     "HeaderProgram": "Program",
     "HeaderClients": "Klienter",
     "LabelCompleted": "Klar",
-    "LabelFailed": "Failed",
+    "LabelFailed": "Misslyckades",
     "LabelSkipped": "Hoppades \u00f6ver",
     "HeaderEpisodeOrganization": "Katalogisering av avsnitt",
-    "LabelSeries": "Series:",
+    "LabelSeries": "Serie:",
     "LabelSeasonNumber": "S\u00e4songsnummer:",
     "LabelEpisodeNumber": "Avsnittsnummer:",
     "LabelEndingEpisodeNumber": "Avslutande avsnittsnummer:",
@@ -633,8 +633,8 @@
     "ButtonScenes": "Scener",
     "ButtonSubtitles": "Undertexter",
     "ButtonAudioTracks": "Ljudsp\u00e5r",
-    "ButtonPreviousTrack": "Previous track",
-    "ButtonNextTrack": "Next track",
+    "ButtonPreviousTrack": "F\u00f6reg\u00e5ende sp\u00e5r:",
+    "ButtonNextTrack": "N\u00e4sta sp\u00e5r:",
     "ButtonStop": "Stopp",
     "ButtonPause": "Paus",
     "LabelGroupMoviesIntoCollections": "Gruppera filmer i samlingsboxar",
@@ -910,7 +910,7 @@
     "LabelContext": "Metod:",
     "OptionContextStreaming": "Str\u00f6mning",
     "OptionContextStatic": "Synk",
-    "ButtonAddToPlaylist": "Add to playlist",
+    "ButtonAddToPlaylist": "L\u00e4gg till i spellista",
     "TabPlaylists": "Playlists",
     "ButtonClose": "St\u00e4ng",
     "LabelAllLanguages": "All languages",
@@ -934,19 +934,19 @@
     "OptionMenu": "Menu",
     "OptionScreenshot": "Screenshot",
     "OptionLocked": "Locked",
-    "OptionUnidentified": "Unidentified",
-    "OptionMissingParentalRating": "Missing parental rating",
+    "OptionUnidentified": "Oidentifierad",
+    "OptionMissingParentalRating": "\u00c5ldersgr\u00e4ns saknas",
     "OptionStub": "Stub",
-    "HeaderEpisodes": "Episodes:",
-    "OptionSeason0": "Season 0",
+    "HeaderEpisodes": "Avsnitt:",
+    "OptionSeason0": "S\u00e4song 0",
     "LabelReport": "Report:",
-    "OptionReportSongs": "Songs",
-    "OptionReportSeries": "Series",
-    "OptionReportSeasons": "Seasons",
+    "OptionReportSongs": "L\u00e5tar",
+    "OptionReportSeries": "Serier",
+    "OptionReportSeasons": "S\u00e4songer",
     "OptionReportTrailers": "Trailers",
-    "OptionReportMusicVideos": "Music videos",
-    "OptionReportMovies": "Movies",
-    "OptionReportHomeVideos": "Home videos",
+    "OptionReportMusicVideos": "Musikvideos",
+    "OptionReportMovies": "Filmer",
+    "OptionReportHomeVideos": "Hemvideos",
     "OptionReportGames": "Games",
     "OptionReportEpisodes": "Episodes",
     "OptionReportCollections": "Collections",

+ 2 - 126
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Api;
+using System.Net;
+using MediaBrowser.Api;
 using MediaBrowser.Common;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Events;
@@ -310,20 +311,6 @@ namespace MediaBrowser.ServerApplication
                 saveConfig = true;
             }
 
-            if (ServerConfigurationManager.Configuration.ChapterOptions != null)
-            {
-                ServerConfigurationManager.SaveConfiguration("chapters", ServerConfigurationManager.Configuration.ChapterOptions);
-                ServerConfigurationManager.Configuration.ChapterOptions = null;
-                saveConfig = true;
-            }
-
-            if (ServerConfigurationManager.Configuration.ChannelOptions != null)
-            {
-                ServerConfigurationManager.SaveConfiguration("channels", ServerConfigurationManager.Configuration.ChannelOptions);
-                ServerConfigurationManager.Configuration.ChannelOptions = null;
-                saveConfig = true;
-            }
-
             if (ServerConfigurationManager.Configuration.NotificationOptions != null)
             {
                 ServerConfigurationManager.SaveConfiguration("notifications", ServerConfigurationManager.Configuration.NotificationOptions);
@@ -385,117 +372,6 @@ namespace MediaBrowser.ServerApplication
             {
                 // Not there, no big deal
             }
-
-            Task.Run(() =>
-            {
-                try
-                {
-                    Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "remote-images"), true);
-                }
-                catch (IOException)
-                {
-                    // Not there, no big deal
-                }
-
-                try
-                {
-                    Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "chapter-images"), true);
-                }
-                catch (IOException)
-                {
-                    // Not there, no big deal
-                }
-
-                try
-                {
-                    Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "extracted-video-images"), true);
-                }
-                catch (IOException)
-                {
-                    // Not there, no big deal
-                }
-
-                try
-                {
-                    Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "extracted-audio-images"), true);
-                }
-                catch (IOException)
-                {
-                    // Not there, no big deal
-                }
-
-                try
-                {
-                    Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "tmdb-tv"), true);
-                }
-                catch (IOException)
-                {
-                    // Not there, no big deal
-                }
-
-                try
-                {
-                    Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "tmdb-collections"), true);
-                }
-                catch (IOException)
-                {
-                    // Not there, no big deal
-                }
-
-                try
-                {
-                    Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "tmdb-movies"), true);
-                }
-                catch (IOException)
-                {
-                    // Not there, no big deal
-                }
-
-                try
-                {
-                    Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "fanart-movies"), true);
-                }
-                catch (IOException)
-                {
-                    // Not there, no big deal
-                }
-
-                try
-                {
-                    Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "fanart-music"), true);
-                }
-                catch (IOException)
-                {
-                    // Not there, no big deal
-                }
-
-                try
-                {
-                    Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "fanart-tv"), true);
-                }
-                catch (IOException)
-                {
-                    // Not there, no big deal
-                }
-
-                try
-                {
-                    Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "tmdb-people"), true);
-                }
-                catch (IOException)
-                {
-                    // Not there, no big deal
-                }
-
-                try
-                {
-                    Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "tvdb-v3"), true);
-                }
-                catch (IOException)
-                {
-                    // Not there, no big deal
-                }
-            });
         }
 
         private void MigrateUserFolders()

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

@@ -231,6 +231,8 @@ namespace MediaBrowser.WebDashboard.Api
                 }    
             }
 
+            path = path.Replace("scripts/jquery.mobile-1.4.3.min.map", "thirdparty/jquerymobile-1.4.3/jquery.mobile-1.4.3.min.map", StringComparison.OrdinalIgnoreCase);
+
             var localizationCulture = GetLocalizationCulture();
 
             // Don't cache if not configured to do so

部分文件因为文件数量过多而无法显示