Sfoglia il codice sorgente

Merge pull request #2101 from MediaBrowser/beta

Beta
Luke 8 anni fa
parent
commit
ba9577f380
78 ha cambiato i file con 964 aggiunte e 824 eliminazioni
  1. 10 38
      Emby.Drawing/GDI/DynamicImageHelpers.cs
  2. 5 3
      Emby.Drawing/GDI/GDIImageEncoder.cs
  3. 8 6
      MediaBrowser.Api/Library/FileOrganizationService.cs
  4. 2 1
      MediaBrowser.Api/Movies/MoviesService.cs
  5. 99 51
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  6. 0 5
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  7. 0 2
      MediaBrowser.Api/Playback/StreamRequest.cs
  8. 0 2
      MediaBrowser.Api/Reports/ReportsService.cs
  9. 13 0
      MediaBrowser.Api/Sync/SyncHelper.cs
  10. 2 1
      MediaBrowser.Api/Sync/SyncService.cs
  11. 0 2
      MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
  12. 0 20
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  13. 0 1
      MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs
  14. 18 4
      MediaBrowser.Controller/Entities/BaseItem.cs
  15. 1 0
      MediaBrowser.Controller/Entities/CollectionFolder.cs
  16. 5 16
      MediaBrowser.Controller/Entities/Folder.cs
  17. 25 21
      MediaBrowser.Controller/Entities/TV/Episode.cs
  18. 6 2
      MediaBrowser.Controller/Entities/TV/Season.cs
  19. 23 1
      MediaBrowser.Controller/FileOrganization/IFileOrganizationService.cs
  20. 1 0
      MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
  21. 6 1
      MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
  22. 9 0
      MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
  23. 9 3
      MediaBrowser.Controller/Session/ISessionManager.cs
  24. 5 5
      MediaBrowser.Dlna/PlayTo/Device.cs
  25. 1 1
      MediaBrowser.Dlna/PlayTo/PlayToController.cs
  26. 0 1
      MediaBrowser.Dlna/Profiles/DefaultProfile.cs
  27. 0 2
      MediaBrowser.Dlna/Profiles/KodiProfile.cs
  28. 7 0
      MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
  29. 75 43
      MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
  30. 4 0
      MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
  31. 8 8
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  32. 2 0
      MediaBrowser.Model/Configuration/EncodingOptions.cs
  33. 3 0
      MediaBrowser.Model/Configuration/LibraryOptions.cs
  34. 2 0
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  35. 0 3
      MediaBrowser.Model/Configuration/UserConfiguration.cs
  36. 5 1
      MediaBrowser.Model/Dlna/AudioOptions.cs
  37. 1 2
      MediaBrowser.Model/Dlna/DeviceProfile.cs
  38. 26 22
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  39. 1 3
      MediaBrowser.Model/Dlna/StreamInfo.cs
  40. 0 3
      MediaBrowser.Model/Dlna/TranscodingProfile.cs
  41. 2 1
      MediaBrowser.Model/Entities/MetadataProviders.cs
  42. 6 0
      MediaBrowser.Model/FileOrganization/FileOrganizationResult.cs
  43. 1 1
      MediaBrowser.Model/Net/MimeTypes.cs
  44. 0 4
      MediaBrowser.Model/Querying/ItemFilter.cs
  45. 6 3
      MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
  46. 1 1
      MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
  47. 1 1
      MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
  48. 6 3
      MediaBrowser.Providers/Photos/PhotoProvider.cs
  49. 7 10
      MediaBrowser.Server.Implementations/EntryPoints/RecordingNotifier.cs
  50. 2 1
      MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs
  51. 16 0
      MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
  52. 68 0
      MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs
  53. 94 6
      MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs
  54. 18 2
      MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
  55. 21 19
      MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
  56. 0 6
      MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
  57. 2 0
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
  58. 1 1
      MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
  59. 1 2
      MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
  60. 6 3
      MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
  61. 9 2
      MediaBrowser.Server.Implementations/Session/SessionManager.cs
  62. 5 0
      MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
  63. 48 0
      MediaBrowser.Server.Implementations/Sync/SyncNotificationEntryPoint.cs
  64. 71 323
      MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
  65. 4 25
      MediaBrowser.Server.Mono/Native/BaseMonoApp.cs
  66. 1 2
      MediaBrowser.Server.Startup.Common/ApplicationHost.cs
  67. 1 2
      MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
  68. 0 40
      MediaBrowser.Server.Startup.Common/Migrations/CollectionGroupingMigration.cs
  69. 0 40
      MediaBrowser.Server.Startup.Common/Migrations/FolderViewSettingMigration.cs
  70. 97 0
      MediaBrowser.Server.Startup.Common/Migrations/UpdateLevelMigration.cs
  71. 38 26
      MediaBrowser.WebDashboard/Api/DashboardService.cs
  72. 26 20
      MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
  73. 9 0
      MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
  74. 6 0
      MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
  75. 3 1
      MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
  76. 2 2
      Nuget/MediaBrowser.Common.Internal.nuspec
  77. 1 1
      Nuget/MediaBrowser.Common.nuspec
  78. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec

+ 10 - 38
Emby.Drawing/GDI/DynamicImageHelpers.cs

@@ -33,7 +33,9 @@ namespace Emby.Drawing.GDI
                     graphics.SmoothingMode = SmoothingMode.HighQuality;
                     graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                     graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
-                    graphics.CompositingMode = CompositingMode.SourceCopy;
+
+                    // SourceCopy causes the image to be blank in OSX
+                    //graphics.CompositingMode = CompositingMode.SourceCopy;
 
                     for (var row = 0; row < rows; row++)
                     {
@@ -44,19 +46,9 @@ namespace Emby.Drawing.GDI
 
                             if (files.Count > index)
                             {
-                                using (var fileStream = fileSystem.GetFileStream(files[index], FileMode.Open, FileAccess.Read, FileShare.Read, true))
+                                using (var imgtemp = Image.FromFile(files[index]))
                                 {
-                                    using (var memoryStream = new MemoryStream())
-                                    {
-                                        fileStream.CopyTo(memoryStream);
-
-                                        memoryStream.Position = 0;
-
-                                        using (var imgtemp = Image.FromStream(memoryStream, true, false))
-                                        {
-                                            graphics.DrawImage(imgtemp, x, y, cellWidth, cellHeight);
-                                        }
-                                    }
+                                    graphics.DrawImage(imgtemp, x, y, cellWidth, cellHeight);
                                 }
                             }
 
@@ -90,7 +82,9 @@ namespace Emby.Drawing.GDI
                     graphics.SmoothingMode = SmoothingMode.HighQuality;
                     graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                     graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
-                    graphics.CompositingMode = CompositingMode.SourceCopy;
+
+                    // SourceCopy causes the image to be blank in OSX
+                    //graphics.CompositingMode = CompositingMode.SourceCopy;
 
                     for (var row = 0; row < rows; row++)
                     {
@@ -99,21 +93,10 @@ namespace Emby.Drawing.GDI
                             var x = col * singleSize;
                             var y = row * singleSize;
 
-                            using (var fileStream = fileSystem.GetFileStream(files[index], FileMode.Open, FileAccess.Read, FileShare.Read, true))
+                            using (var imgtemp = Image.FromFile(files[index]))
                             {
-                                using (var memoryStream = new MemoryStream())
-                                {
-                                    fileStream.CopyTo(memoryStream);
-
-                                    memoryStream.Position = 0;
-
-                                    using (var imgtemp = Image.FromStream(memoryStream, true, false))
-                                    {
-                                        graphics.DrawImage(imgtemp, x, y, singleSize, singleSize);
-                                    }
-                                }
+                                graphics.DrawImage(imgtemp, x, y, singleSize, singleSize);
                             }
-
                             index++;
                         }
                     }
@@ -121,16 +104,5 @@ namespace Emby.Drawing.GDI
                 }
             }
         }
-
-        private static Stream GetStream(Image image)
-        {
-            var ms = new MemoryStream();
-
-            image.Save(ms, ImageFormat.Png);
-
-            ms.Position = 0;
-
-            return ms;
-        }
     }
 }

+ 5 - 3
Emby.Drawing/GDI/GDIImageEncoder.cs

@@ -119,9 +119,11 @@ namespace Emby.Drawing.GDI
                         thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality;
                         thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
                         thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
-                        thumbnailGraph.CompositingMode = !hasPostProcessing ?
-                            CompositingMode.SourceCopy :
-                            CompositingMode.SourceOver;
+
+                        // SourceCopy causes the image to be blank in OSX
+                        //thumbnailGraph.CompositingMode = !hasPostProcessing ?
+                        //    CompositingMode.SourceCopy :
+                        //    CompositingMode.SourceOver;
 
                         SetBackgroundColor(thumbnailGraph, options);
 

+ 8 - 6
MediaBrowser.Api/Library/FileOrganizationService.cs

@@ -154,9 +154,12 @@ namespace MediaBrowser.Api.Library
 
         public void Post(PerformOrganization request)
         {
+            // Don't await this
             var task = _iFileOrganizationService.PerformOrganization(request.Id);
 
-            Task.WaitAll(task);
+            // Async processing (close dialog early instead of waiting until the file has been copied)
+            // Wait 2s for exceptions that may occur to have them forwarded to the client for immediate error display
+            task.Wait(2000);
         }
 
         public void Post(OrganizeEpisode request)
@@ -168,6 +171,7 @@ namespace MediaBrowser.Api.Library
                 dicNewProviderIds = request.NewSeriesProviderIds;
             }
 
+            // Don't await this
             var task = _iFileOrganizationService.PerformEpisodeOrganization(new EpisodeFileOrganizationRequest
             {
                 EndingEpisodeNumber = request.EndingEpisodeNumber,
@@ -182,11 +186,9 @@ namespace MediaBrowser.Api.Library
                 TargetFolder = request.TargetFolder
             });
 
-            // For async processing (close dialog early instead of waiting until the file has been copied)
-            //var tasks = new Task[] { task };
-            //Task.WaitAll(tasks, 8000);
-
-            Task.WaitAll(task);
+            // Async processing (close dialog early instead of waiting until the file has been copied)
+            // Wait 2s for exceptions that may occur to have them forwarded to the client for immediate error display
+            task.Wait(2000);
         }
 
         public object Get(GetSmartMatchInfos request)

+ 2 - 1
MediaBrowser.Api/Movies/MoviesService.cs

@@ -192,7 +192,8 @@ namespace MediaBrowser.Api.Movies
                 SortOrder = SortOrder.Descending,
                 Limit = 7,
                 ParentId = parentIdGuid,
-                Recursive = true
+                Recursive = true,
+                IsPlayed = true
             };
 
             var recentlyPlayedMovies = _libraryManager.GetItemList(query).ToList();

+ 99 - 51
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -298,7 +298,8 @@ namespace MediaBrowser.Api.Playback
             // Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this.
             if (state.VideoType == VideoType.VideoFile)
             {
-                var hwType = ApiEntryPoint.Instance.GetEncodingOptions().HardwareAccelerationType;
+                var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
+                var hwType = encodingOptions.HardwareAccelerationType;
 
                 if (string.Equals(hwType, "qsv", StringComparison.OrdinalIgnoreCase) ||
                     string.Equals(hwType, "h264_qsv", StringComparison.OrdinalIgnoreCase))
@@ -314,6 +315,10 @@ namespace MediaBrowser.Api.Playback
                 {
                     return GetAvailableEncoder("h264_omx", defaultEncoder);
                 }
+                if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(encodingOptions.VaapiDevice))
+                {
+                    return GetAvailableEncoder("h264_vaapi", defaultEncoder);
+                }
             }
 
             return defaultEncoder;
@@ -427,7 +432,8 @@ namespace MediaBrowser.Api.Playback
 
             if (!string.IsNullOrEmpty(state.VideoRequest.Profile))
             {
-                if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase))
+                if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
+                    !string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
                 {
                     // not supported by h264_omx
                     param += " -profile:v " + state.VideoRequest.Profile;
@@ -482,7 +488,8 @@ namespace MediaBrowser.Api.Playback
 
             if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
                 !string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
-                !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
+                !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
+                !string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
             {
                 param = "-pix_fmt yuv420p " + param;
             }
@@ -548,59 +555,97 @@ namespace MediaBrowser.Api.Playback
 
             var filters = new List<string>();
 
-            if (state.DeInterlace)
+            if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
+            {
+                filters.Add("format=nv12|vaapi");
+                filters.Add("hwupload");
+            }
+            else if (state.DeInterlace && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
             {
                 filters.Add("yadif=0:-1:0");
             }
 
-            // If fixed dimensions were supplied
-            if (request.Width.HasValue && request.Height.HasValue)
+            if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
             {
-                var widthParam = request.Width.Value.ToString(UsCulture);
-                var heightParam = request.Height.Value.ToString(UsCulture);
+                // Work around vaapi's reduced scaling features
+                var scaler = "scale_vaapi";
 
-                filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
-            }
+                // Given the input dimensions (inputWidth, inputHeight), determine the output dimensions
+                // (outputWidth, outputHeight). The user may request precise output dimensions or maximum
+                // output dimensions. Output dimensions are guaranteed to be even.
+                decimal inputWidth = Convert.ToDecimal(state.VideoStream.Width);
+                decimal inputHeight = Convert.ToDecimal(state.VideoStream.Height);
+                decimal outputWidth = request.Width.HasValue ? Convert.ToDecimal(request.Width.Value) : inputWidth;
+                decimal outputHeight = request.Height.HasValue ? Convert.ToDecimal(request.Height.Value) : inputHeight;
+                decimal maximumWidth = request.MaxWidth.HasValue ? Convert.ToDecimal(request.MaxWidth.Value) : outputWidth;
+                decimal maximumHeight = request.MaxHeight.HasValue ? Convert.ToDecimal(request.MaxHeight.Value) : outputHeight;
 
-            // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
-            else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
-            {
-                var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
-                var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+                if (outputWidth > maximumWidth || outputHeight > maximumHeight)
+                {
+                    var scale = Math.Min(maximumWidth / outputWidth, maximumHeight / outputHeight);
+                    outputWidth = Math.Min(maximumWidth, Math.Truncate(outputWidth * scale));
+                    outputHeight = Math.Min(maximumHeight, Math.Truncate(outputHeight * scale));
+                }
 
-                filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
-            }
+                outputWidth = 2 * Math.Truncate(outputWidth / 2);
+                outputHeight = 2 * Math.Truncate(outputHeight / 2);
 
-            // If a fixed width was requested
-            else if (request.Width.HasValue)
+                if (outputWidth != inputWidth || outputHeight != inputHeight)
+                {
+                    filters.Add(string.Format("{0}=w={1}:h={2}", scaler, outputWidth.ToString(UsCulture), outputHeight.ToString(UsCulture)));
+                }
+            }
+            else
             {
-                var widthParam = request.Width.Value.ToString(UsCulture);
+                // If fixed dimensions were supplied
+                if (request.Width.HasValue && request.Height.HasValue)
+                {
+                    var widthParam = request.Width.Value.ToString(UsCulture);
+                    var heightParam = request.Height.Value.ToString(UsCulture);
 
-                filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
-            }
+                    filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
+                }
 
-            // If a fixed height was requested
-            else if (request.Height.HasValue)
-            {
-                var heightParam = request.Height.Value.ToString(UsCulture);
+                // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
+                else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
+                {
+                    var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+                    var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
 
-                filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
-            }
+                    filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
+                }
 
-            // If a max width was requested
-            else if (request.MaxWidth.HasValue)
-            {
-                var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+                // If a fixed width was requested
+                else if (request.Width.HasValue)
+                {
+                    var widthParam = request.Width.Value.ToString(UsCulture);
 
-                filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
-            }
+                    filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
+                }
 
-            // If a max height was requested
-            else if (request.MaxHeight.HasValue)
-            {
-                var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+                // If a fixed height was requested
+                else if (request.Height.HasValue)
+                {
+                    var heightParam = request.Height.Value.ToString(UsCulture);
 
-                filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
+                    filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
+                }
+
+                // If a max width was requested
+                else if (request.MaxWidth.HasValue)
+                {
+                    var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+
+                    filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
+                }
+
+                // If a max height was requested
+                else if (request.MaxHeight.HasValue)
+                {
+                    var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+
+                    filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(max(iw/dar\\,ih)\\,{0})", maxHeightParam));
+                }
             }
 
             var output = string.Empty;
@@ -935,6 +980,17 @@ namespace MediaBrowser.Api.Playback
                 }
             }
 
+            if (state.VideoRequest != null)
+            {
+                var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
+                if (GetVideoEncoder(state).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1)
+                {
+                    arg = "-hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
+                }
+            }
+
+            arg += string.Format(" -ss {0}", MediaEncoder.GetTimeParameter(TimeSpan.FromSeconds(1).Ticks)); 
+
             return arg.Trim();
         }
 
@@ -1589,13 +1645,6 @@ namespace MediaBrowser.Api.Playback
                     }
                 }
                 else if (i == 25)
-                {
-                    if (videoRequest != null)
-                    {
-                        videoRequest.ForceLiveStream = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
-                    }
-                }
-                else if (i == 26)
                 {
                     if (!string.IsNullOrWhiteSpace(val) && videoRequest != null)
                     {
@@ -1606,18 +1655,18 @@ namespace MediaBrowser.Api.Playback
                         }
                     }
                 }
-                else if (i == 27)
+                else if (i == 26)
                 {
                     request.TranscodingMaxAudioChannels = int.Parse(val, UsCulture);
                 }
-                else if (i == 28)
+                else if (i == 27)
                 {
                     if (videoRequest != null)
                     {
                         videoRequest.EnableSubtitlesInManifest = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
                     }
                 }
-                else if (i == 29)
+                else if (i == 28)
                 {
                     request.Tag = val;
                 }
@@ -2218,7 +2267,6 @@ namespace MediaBrowser.Api.Playback
                     if (state.VideoRequest != null)
                     {
                         state.VideoRequest.CopyTimestamps = transcodingProfile.CopyTimestamps;
-                        state.VideoRequest.ForceLiveStream = transcodingProfile.ForceLiveStream;
                         state.VideoRequest.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
                     }
                 }
@@ -2244,7 +2292,7 @@ namespace MediaBrowser.Api.Playback
                 return Task.FromResult(true);
             }
 
-            if (!string.Equals(MediaEncoder.EncoderLocationType, "Default", StringComparison.OrdinalIgnoreCase))
+            if (!MediaEncoder.IsDefaultEncoderPath)
             {
                 return Task.FromResult(true);
             }

+ 0 - 5
MediaBrowser.Api/Playback/Hls/BaseHlsService.cs

@@ -281,11 +281,6 @@ namespace MediaBrowser.Api.Playback.Hls
         {
             var isLiveStream = (state.RunTimeTicks ?? 0) == 0;
 
-            if (state.VideoRequest.ForceLiveStream)
-            {
-                return true;
-            }
-
             return isLiveStream;
         }
     }

+ 0 - 2
MediaBrowser.Api/Playback/StreamRequest.cs

@@ -193,8 +193,6 @@ namespace MediaBrowser.Api.Playback
         [ApiMember(Name = "CopyTimestamps", Description = "Whether or not to copy timestamps when transcoding with an offset. Defaults to false.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
         public bool CopyTimestamps { get; set; }
 
-        public bool ForceLiveStream { get; set; }
-
         public bool EnableSubtitlesInManifest { get; set; }
 
         public VideoStreamRequest()

+ 0 - 2
MediaBrowser.Api/Reports/ReportsService.cs

@@ -275,8 +275,6 @@ namespace MediaBrowser.Api.Reports
                     case ItemFilter.IsPlayed:
                         query.IsPlayed = true;
                         break;
-                    case ItemFilter.IsRecentlyAdded:
-                        break;
                     case ItemFilter.IsResumable:
                         query.IsResumable = true;
                         break;

+ 13 - 0
MediaBrowser.Api/Sync/SyncHelper.cs

@@ -24,6 +24,19 @@ namespace MediaBrowser.Api.Sync
                         }
                         break;
                     }
+                    if (item.IsAudio)
+                    {
+                        options.Add(SyncJobOption.Quality);
+                        options.Add(SyncJobOption.Profile);
+                        break;
+                    }
+                    if (item.IsMusicGenre || item.IsArtist|| item.IsType("musicalbum"))
+                    {
+                        options.Add(SyncJobOption.Quality);
+                        options.Add(SyncJobOption.Profile);
+                        options.Add(SyncJobOption.ItemLimit);
+                        break;
+                    }
                     if (item.IsFolderItem && !item.IsMusicGenre && !item.IsArtist && !item.IsType("musicalbum") && !item.IsGameGenre)
                     {
                         options.Add(SyncJobOption.Quality);

+ 2 - 1
MediaBrowser.Api/Sync/SyncService.cs

@@ -291,7 +291,8 @@ namespace MediaBrowser.Api.Sync
                 {
                     Fields = new List<ItemFields>
                     {
-                        ItemFields.SyncInfo
+                        ItemFields.SyncInfo,
+                        ItemFields.BasicSyncInfo
                     }
                 };
 

+ 0 - 2
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -164,8 +164,6 @@ namespace MediaBrowser.Api.UserLibrary
                     case ItemFilter.IsPlayed:
                         query.IsPlayed = true;
                         break;
-                    case ItemFilter.IsRecentlyAdded:
-                        break;
                     case ItemFilter.IsResumable:
                         query.IsResumable = true;
                         break;

+ 0 - 20
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -149,24 +149,6 @@ namespace MediaBrowser.Api.UserLibrary
                 item = user == null ? _libraryManager.RootFolder : user.RootFolder;
             }
 
-            if (!string.IsNullOrEmpty(request.Ids))
-            {
-                var query = GetItemsQuery(request, user);
-                var specificItems = _libraryManager.GetItemList(query).ToArray();
-                if (query.SortBy.Length == 0)
-                {
-                    var ids = query.ItemIds.ToList();
-
-                    // Try to preserve order
-                    specificItems = specificItems.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray();
-                }
-                return new QueryResult<BaseItem>
-                {
-                    Items = specificItems.ToArray(),
-                    TotalRecordCount = specificItems.Length
-                };
-            }
-
             // Default list type = children
 
             var folder = item as Folder;
@@ -289,8 +271,6 @@ namespace MediaBrowser.Api.UserLibrary
                     case ItemFilter.IsPlayed:
                         query.IsPlayed = true;
                         break;
-                    case ItemFilter.IsRecentlyAdded:
-                        break;
                     case ItemFilter.IsResumable:
                         query.IsResumable = true;
                         break;

+ 0 - 1
MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs

@@ -33,7 +33,6 @@ namespace MediaBrowser.Common.Implementations.Updates
                 EnableKeepAlive = false,
                 CancellationToken = cancellationToken,
                 UserAgent = "Emby/3.0"
-
             };
 
             if (_cacheLength.Ticks > 0)

+ 18 - 4
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -281,6 +281,20 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        public Task UpdateIsOffline(bool newValue)
+        {
+            var item = this;
+
+            if (item.IsOffline != newValue)
+            {
+                item.IsOffline = newValue;
+                // this is creating too many repeated db updates
+                //return item.UpdateToRepository(ItemUpdateType.None, CancellationToken.None);
+            }
+
+            return Task.FromResult(true);
+        }
+
         /// <summary>
         /// Gets or sets the type of the location.
         /// </summary>
@@ -290,10 +304,10 @@ namespace MediaBrowser.Controller.Entities
         {
             get
             {
-                if (IsOffline)
-                {
-                    return LocationType.Offline;
-                }
+                //if (IsOffline)
+                //{
+                //    return LocationType.Offline;
+                //}
 
                 if (string.IsNullOrWhiteSpace(Path))
                 {

+ 1 - 0
MediaBrowser.Controller/Entities/CollectionFolder.cs

@@ -106,6 +106,7 @@ namespace MediaBrowser.Controller.Entities
             {
                 LibraryOptions[path] = options;
 
+                options.SchemaVersion = 1;
                 XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path));
             }
         }

+ 5 - 16
MediaBrowser.Controller/Entities/Folder.cs

@@ -375,7 +375,7 @@ namespace MediaBrowser.Controller.Entities
 
                     if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child))
                     {
-                        await UpdateIsOffline(currentChild, false).ConfigureAwait(false);
+                        await currentChild.UpdateIsOffline(false).ConfigureAwait(false);
                         validChildren.Add(currentChild);
 
                         continue;
@@ -404,7 +404,7 @@ namespace MediaBrowser.Controller.Entities
 
                         else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path))
                         {
-                            await UpdateIsOffline(item, true).ConfigureAwait(false);
+                            await item.UpdateIsOffline(true).ConfigureAwait(false);
                         }
                         else
                         {
@@ -461,17 +461,6 @@ namespace MediaBrowser.Controller.Entities
             progress.Report(100);
         }
 
-        private Task UpdateIsOffline(BaseItem item, bool newValue)
-        {
-            if (item.IsOffline != newValue)
-            {
-                item.IsOffline = newValue;
-                return item.UpdateToRepository(ItemUpdateType.None, CancellationToken.None);
-            }
-
-            return Task.FromResult(true);
-        }
-
         private async Task RefreshMetadataRecursive(MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
         {
             var children = ActualChildren.ToList();
@@ -902,16 +891,16 @@ namespace MediaBrowser.Controller.Entities
         {
             if (query.ItemIds.Length > 0)
             {
-                var specificItems = query.ItemIds.Select(LibraryManager.GetItemById).Where(i => i != null).ToList();
+                var result = LibraryManager.GetItemsResult(query);
 
                 if (query.SortBy.Length == 0)
                 {
                     var ids = query.ItemIds.ToList();
 
                     // Try to preserve order
-                    specificItems = specificItems.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToList();
+                    result.Items = result.Items.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray();
                 }
-                return Task.FromResult(PostFilterAndSort(specificItems, query, true, true));
+                return Task.FromResult(result);
             }
 
             return GetItemsInternal(query);

+ 25 - 21
MediaBrowser.Controller/Entities/TV/Episode.cs

@@ -135,7 +135,11 @@ namespace MediaBrowser.Controller.Entities.TV
         [IgnoreDataMember]
         public Series Series
         {
-            get { return FindParent<Series>(); }
+            get
+            {
+                var seriesId = SeriesId ?? FindSeriesId();
+                return seriesId.HasValue ? (LibraryManager.GetItemById(seriesId.Value) as Series) : null;
+            }
         }
 
         [IgnoreDataMember]
@@ -143,24 +147,8 @@ namespace MediaBrowser.Controller.Entities.TV
         {
             get
             {
-                var season = FindParent<Season>();
-
-                // Episodes directly in series folder
-                if (season == null)
-                {
-                    var series = Series;
-
-                    if (series != null && ParentIndexNumber.HasValue)
-                    {
-                        var findNumber = ParentIndexNumber.Value;
-
-                        season = series.Children
-                            .OfType<Season>()
-                            .FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == findNumber);
-                    }
-                }
-
-                return season;
+                var seasonId = SeasonId ?? FindSeasonId();
+                return seasonId.HasValue ? (LibraryManager.GetItemById(seasonId.Value) as Season) : null;
             }
         }
 
@@ -193,7 +181,23 @@ namespace MediaBrowser.Controller.Entities.TV
 
         public Guid? FindSeasonId()
         {
-            var season = Season;
+            var season = FindParent<Season>();
+
+            // Episodes directly in series folder
+            if (season == null)
+            {
+                var series = Series;
+
+                if (series != null && ParentIndexNumber.HasValue)
+                {
+                    var findNumber = ParentIndexNumber.Value;
+
+                    season = series.Children
+                        .OfType<Season>()
+                        .FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == findNumber);
+                }
+            }
+
             return season == null ? (Guid?)null : season.Id;
         }
 
@@ -263,7 +267,7 @@ namespace MediaBrowser.Controller.Entities.TV
 
         public Guid? FindSeriesId()
         {
-            var series = Series;
+            var series = FindParent<Series>();
             return series == null ? (Guid?)null : series.Id;
         }
 

+ 6 - 2
MediaBrowser.Controller/Entities/TV/Season.cs

@@ -99,7 +99,11 @@ namespace MediaBrowser.Controller.Entities.TV
         [IgnoreDataMember]
         public Series Series
         {
-            get { return FindParent<Series>(); }
+            get
+            {
+                var seriesId = SeriesId ?? FindSeriesId();
+                return seriesId.HasValue ? (LibraryManager.GetItemById(seriesId.Value) as Series) : null;
+            }
         }
 
         [IgnoreDataMember]
@@ -241,7 +245,7 @@ namespace MediaBrowser.Controller.Entities.TV
 
         public Guid? FindSeriesId()
         {
-            var series = Series;
+            var series = FindParent<Series>();
             return series == null ? (Guid?)null : series.Id;
         }
 

+ 23 - 1
MediaBrowser.Controller/FileOrganization/IFileOrganizationService.cs

@@ -1,5 +1,7 @@
-using MediaBrowser.Model.FileOrganization;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.FileOrganization;
 using MediaBrowser.Model.Querying;
+using System;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -7,6 +9,11 @@ namespace MediaBrowser.Controller.FileOrganization
 {
     public interface IFileOrganizationService
     {
+        event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemAdded;
+        event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemUpdated;
+        event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemRemoved;
+        event EventHandler LogReset;
+
         /// <summary>
         /// Processes the new files.
         /// </summary>
@@ -81,5 +88,20 @@ namespace MediaBrowser.Controller.FileOrganization
         /// <param name="ItemName">Item name.</param>
         /// <param name="matchString">The match string to delete.</param>
         void DeleteSmartMatchEntry(string ItemName, string matchString);
+
+        /// <summary>
+        /// Attempts to add a an item to the list of currently processed items.
+        /// </summary>
+        /// <param name="result">The result item.</param>
+        /// <param name="fullClientRefresh">Passing true will notify the client to reload all items, otherwise only a single item will be refreshed.</param>
+        /// <returns>True if the item was added, False if the item is already contained in the list.</returns>
+        bool AddToInProgressList(FileOrganizationResult result, bool fullClientRefresh);
+
+        /// <summary>
+        /// Removes an item from the list of currently processed items.
+        /// </summary>
+        /// <param name="result">The result item.</param>
+        /// <returns>True if the item was removed, False if the item was not contained in the list.</returns>
+        bool RemoveFromInprogressList(FileOrganizationResult result);
     }
 }

+ 1 - 0
MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs

@@ -134,5 +134,6 @@ namespace MediaBrowser.Controller.MediaEncoding
 
         Task UpdateEncoderPath(string path, string pathType);
         bool SupportsEncoder(string encoder);
+        bool IsDefaultEncoderPath { get; }
     }
 }

+ 6 - 1
MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs

@@ -36,8 +36,13 @@ namespace MediaBrowser.Controller.MediaEncoding
             return new[] {videoPath};
         }
 
-        public static List<string> GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, IEnumerable<string> filenames)
+        private static List<string> GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, List<string> filenames)
         {
+            if (filenames.Count == 0)
+            {
+                return new List<string>();
+            }
+
             var allFiles = fileSystem
                 .GetFilePaths(rootPath, true)
                 .ToList();

+ 9 - 0
MediaBrowser.Controller/Providers/BaseItemXmlParser.cs

@@ -724,6 +724,15 @@ namespace MediaBrowser.Controller.Providers
                         }
                         break;
                     }
+                case "TvMazeId":
+                    {
+                        var id = reader.ReadElementContentAsString();
+                        if (!string.IsNullOrWhiteSpace(id))
+                        {
+                            item.SetProviderId(MetadataProviders.TvMaze, id);
+                        }
+                        break;
+                    }
                 case "AudioDbArtistId":
                     {
                         var id = reader.ReadElementContentAsString();

+ 9 - 3
MediaBrowser.Controller/Session/ISessionManager.cs

@@ -172,15 +172,21 @@ namespace MediaBrowser.Controller.Session
         Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken);
 
         /// <summary>
-        /// Sends the message to user sessions.
+        /// Sends the message to admin sessions.
         /// </summary>
         /// <typeparam name="T"></typeparam>
-        /// <param name="userId">The user identifier.</param>
         /// <param name="name">The name.</param>
         /// <param name="data">The data.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task SendMessageToUserSessions<T>(string userId, string name, T data, CancellationToken cancellationToken);
+        Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Sends the message to user sessions.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <returns>Task.</returns>
+        Task SendMessageToUserSessions<T>(List<string> userIds, string name, T data, CancellationToken cancellationToken);
 
         /// <summary>
         /// Sends the message to user device sessions.

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

@@ -479,17 +479,17 @@ namespace MediaBrowser.Dlna.PlayTo
                 _successiveStopCount++;
                 _connectFailureCount++;
 
-                if (_successiveStopCount >= maxSuccessiveStopReturns)
-                {
-                    RestartTimerInactive();
-                }
-                if (_connectFailureCount >= maxSuccessiveStopReturns)
+                if (_connectFailureCount >= 3)
                 {
                     if (OnDeviceUnavailable != null)
                     {
                         OnDeviceUnavailable();
                     }
                 }
+                if (_successiveStopCount >= maxSuccessiveStopReturns)
+                {
+                    RestartTimerInactive();
+                }
             }
             catch (Exception ex)
             {

+ 1 - 1
MediaBrowser.Dlna/PlayTo/PlayToController.cs

@@ -99,11 +99,11 @@ namespace MediaBrowser.Dlna.PlayTo
         public void Init(Device device)
         {
             _device = device;
+            _device.OnDeviceUnavailable = OnDeviceUnavailable;
             _device.PlaybackStart += _device_PlaybackStart;
             _device.PlaybackProgress += _device_PlaybackProgress;
             _device.PlaybackStopped += _device_PlaybackStopped;
             _device.MediaChanged += _device_MediaChanged;
-            _device.OnDeviceUnavailable = OnDeviceUnavailable;
 
             _device.Start();
 

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

@@ -33,7 +33,6 @@ namespace MediaBrowser.Dlna.Profiles
             MaxStreamingBitrate = 20000000;
             MaxStaticBitrate = 20000000;
             MusicStreamingTranscodingBitrate = 192000;
-            MusicSyncBitrate = 192000;
 
             EnableAlbumArtInDidl = false;
 

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

@@ -11,9 +11,7 @@ namespace MediaBrowser.Dlna.Profiles
             Name = "Kodi";
 
             MaxStreamingBitrate = 100000000;
-            MaxStaticBitrate = 100000000;
             MusicStreamingTranscodingBitrate = 1280000;
-            MusicSyncBitrate = 1280000;
 
             TimelineOffsetSeconds = 5;
 

+ 7 - 0
MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs

@@ -553,6 +553,13 @@ namespace MediaBrowser.LocalMetadata.Savers
                 builder.Append("<TVRageId>" + SecurityElement.Escape(externalId) + "</TVRageId>");
             }
 
+            externalId = item.GetProviderId(MetadataProviders.TvMaze);
+
+            if (!string.IsNullOrEmpty(externalId))
+            {
+                builder.Append("<TvMazeId>" + SecurityElement.Escape(externalId) + "</TvMazeId>");
+            }
+
             var hasTagline = item as IHasTaglines;
             if (hasTagline != null)
             {

+ 75 - 43
MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs

@@ -680,7 +680,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             if (!string.IsNullOrEmpty(state.Options.Profile))
             {
-                if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase))
+                if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
+                    !string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
                 {
                     // not supported by h264_omx
                     param += " -profile:v " + state.Options.Profile;
@@ -737,7 +738,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
                 !string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
-                !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
+                !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
+                !string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
             {
                 param = "-pix_fmt yuv420p " + param;
             }
@@ -887,66 +889,96 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             var filters = new List<string>();
 
-            if (state.DeInterlace)
+            if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
+            {
+                filters.Add("format=nv12|vaapi");
+                filters.Add("hwupload");
+            }
+            else if (state.DeInterlace && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
             {
                 filters.Add("yadif=0:-1:0");
             }
 
-            // If fixed dimensions were supplied
-            if (request.Width.HasValue && request.Height.HasValue)
+            if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
             {
-                var widthParam = request.Width.Value.ToString(UsCulture);
-                var heightParam = request.Height.Value.ToString(UsCulture);
+                // Work around vaapi's reduced scaling features
+                var scaler = "scale_vaapi";
 
-                filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
-            }
+                // Given the input dimensions (inputWidth, inputHeight), determine the output dimensions
+                // (outputWidth, outputHeight). The user may request precise output dimensions or maximum
+                // output dimensions. Output dimensions are guaranteed to be even.
+                decimal inputWidth = Convert.ToDecimal(state.VideoStream.Width);
+                decimal inputHeight = Convert.ToDecimal(state.VideoStream.Height);
+                decimal outputWidth = request.Width.HasValue ? Convert.ToDecimal(request.Width.Value) : inputWidth;
+                decimal outputHeight = request.Height.HasValue ? Convert.ToDecimal(request.Height.Value) : inputHeight;
+                decimal maximumWidth = request.MaxWidth.HasValue ? Convert.ToDecimal(request.MaxWidth.Value) : outputWidth;
+                decimal maximumHeight = request.MaxHeight.HasValue ? Convert.ToDecimal(request.MaxHeight.Value) : outputHeight;
 
-            // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
-            else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
-            {
-                var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
-                var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+                if (outputWidth > maximumWidth || outputHeight > maximumHeight)
+                {
+                    var scale = Math.Min(maximumWidth / outputWidth, maximumHeight / outputHeight);
+                    outputWidth = Math.Min(maximumWidth, Math.Truncate(outputWidth * scale));
+                    outputHeight = Math.Min(maximumHeight, Math.Truncate(outputHeight * scale));
+                }
 
-                filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
-            }
+                outputWidth = 2 * Math.Truncate(outputWidth / 2);
+                outputHeight = 2 * Math.Truncate(outputHeight / 2);
 
-            // If a fixed width was requested
-            else if (request.Width.HasValue)
+                if (outputWidth != inputWidth || outputHeight != inputHeight)
+                {
+                    filters.Add(string.Format("{0}=w={1}:h={2}", scaler, outputWidth.ToString(UsCulture), outputHeight.ToString(UsCulture)));
+                }
+            }
+            else
             {
-                var widthParam = request.Width.Value.ToString(UsCulture);
+                // If fixed dimensions were supplied
+                if (request.Width.HasValue && request.Height.HasValue)
+                {
+                    var widthParam = request.Width.Value.ToString(UsCulture);
+                    var heightParam = request.Height.Value.ToString(UsCulture);
 
-                filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
-            }
+                    filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
+                }
 
-            // If a fixed height was requested
-            else if (request.Height.HasValue)
-            {
-                var heightParam = request.Height.Value.ToString(UsCulture);
+                // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
+                else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
+                {
+                    var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+                    var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
 
-                filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
-            }
+                    filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
+                }
 
-            // If a max width was requested
-            else if (request.MaxWidth.HasValue)
-            {
-                var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+                // If a fixed width was requested
+                else if (request.Width.HasValue)
+                {
+                    var widthParam = request.Width.Value.ToString(UsCulture);
 
-                filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
-            }
+                    filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
+                }
 
-            // If a max height was requested
-            else if (request.MaxHeight.HasValue)
-            {
-                var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+                // If a fixed height was requested
+                else if (request.Height.HasValue)
+                {
+                    var heightParam = request.Height.Value.ToString(UsCulture);
 
-                filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
-            }
+                    filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
+                }
 
-            if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
-            {
-                if (filters.Count > 1)
+                // If a max width was requested
+                else if (request.MaxWidth.HasValue)
+                {
+                    var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+
+                    filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
+                }
+
+                // If a max height was requested
+                else if (request.MaxHeight.HasValue)
                 {
-                    //filters[filters.Count - 1] += ":flags=fast_bilinear";
+                    var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+
+                    filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(max(iw/dar\\,ih)\\,{0})", maxHeightParam));
                 }
             }
 

+ 4 - 0
MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs

@@ -586,6 +586,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 {
                     return GetAvailableEncoder(mediaEncoder, "h264_omx", defaultEncoder);
                 }
+                if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(options.VaapiDevice))
+                {
+                    return GetAvailableEncoder(mediaEncoder, "h264_vaapi", defaultEncoder);
+                }
             }
 
             return defaultEncoder;

+ 8 - 8
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -123,20 +123,20 @@ namespace MediaBrowser.MediaEncoding.Encoder
                     return "System";
                 }
 
-                if (IsDefaultPath(FFMpegPath))
-                {
-                    return "Default";
-                }
-
                 return "Custom";
             }
         }
 
-        private bool IsDefaultPath(string path)
+        public bool IsDefaultEncoderPath
         {
-            var parentPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg", "20160410");
+            get
+            {
+                var path = FFMpegPath;
+
+                var parentPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg", "20160410");
 
-            return FileSystem.ContainsSubPath(parentPath, path);
+                return FileSystem.ContainsSubPath(parentPath, path);
+            }
         }
 
         private bool IsSystemInstalledPath(string path)

+ 2 - 0
MediaBrowser.Model/Configuration/EncodingOptions.cs

@@ -10,6 +10,7 @@ namespace MediaBrowser.Model.Configuration
         public int ThrottleDelaySeconds { get; set; }
         public string HardwareAccelerationType { get; set; }
         public string EncoderAppPath { get; set; }
+        public string VaapiDevice { get; set; }
 
         public EncodingOptions()
         {
@@ -17,6 +18,7 @@ namespace MediaBrowser.Model.Configuration
             EnableThrottling = true;
             ThrottleDelaySeconds = 180;
             EncodingThreadCount = -1;
+            VaapiDevice = "/dev/dri/card0";
         }
     }
 }

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

@@ -4,10 +4,13 @@
     {
         public bool EnableArchiveMediaFiles { get; set; }
         public bool EnablePhotos { get; set; }
+        public bool EnableRealtimeMonitor { get; set; }
+        public int SchemaVersion { get; set; }
 
         public LibraryOptions()
         {
             EnablePhotos = true;
+            EnableRealtimeMonitor = true;
         }
     }
 }

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

@@ -74,6 +74,8 @@ namespace MediaBrowser.Model.Configuration
         /// <value>The metadata path.</value>
         public string MetadataPath { get; set; }
 
+        public string LastVersion { get; set; }
+
         /// <summary>
         /// Gets or sets the display name of the season zero.
         /// </summary>

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

@@ -27,8 +27,6 @@ namespace MediaBrowser.Model.Configuration
         public bool DisplayMissingEpisodes { get; set; }
         public bool DisplayUnairedEpisodes { get; set; }
 
-        public bool GroupMoviesIntoBoxSets { get; set; }
-
         public string[] ExcludeFoldersFromGrouping { get; set; }
         public string[] GroupedFolders { get; set; }
 
@@ -48,7 +46,6 @@ namespace MediaBrowser.Model.Configuration
         public bool RememberAudioSelections { get; set; }
         public bool RememberSubtitleSelections { get; set; }
         public bool EnableNextEpisodeAutoPlay { get; set; }
-        public bool DisplayFoldersView { get; set; }
 
         /// <summary>
         /// Initializes a new instance of the <see cref="UserConfiguration" /> class.

+ 5 - 1
MediaBrowser.Model/Dlna/AudioOptions.cs

@@ -59,7 +59,7 @@ namespace MediaBrowser.Model.Dlna
         /// Gets the maximum bitrate.
         /// </summary>
         /// <returns>System.Nullable&lt;System.Int32&gt;.</returns>
-        public int? GetMaxBitrate()
+        public int? GetMaxBitrate(bool isAudio)
         {
             if (MaxBitrate.HasValue)
             {
@@ -70,6 +70,10 @@ namespace MediaBrowser.Model.Dlna
             {
                 if (Context == EncodingContext.Static)
                 {
+                    if (isAudio && Profile.MaxStaticMusicBitrate.HasValue)
+                    {
+                        return Profile.MaxStaticMusicBitrate;
+                    }
                     return Profile.MaxStaticBitrate;
                 }
 

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

@@ -55,7 +55,7 @@ namespace MediaBrowser.Model.Dlna
         public int? MaxStaticBitrate { get; set; }
 
         public int? MusicStreamingTranscodingBitrate { get; set; }
-        public int? MusicSyncBitrate { get; set; }
+        public int? MaxStaticMusicBitrate { get; set; }
 
         /// <summary>
         /// Controls the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.
@@ -115,7 +115,6 @@ namespace MediaBrowser.Model.Dlna
             MaxStreamingBitrate = 8000000;
             MaxStaticBitrate = 8000000;
             MusicStreamingTranscodingBitrate = 128000;
-            MusicSyncBitrate = 128000;
         }
 
         public List<string> GetSupportedMediaTypes()

+ 26 - 22
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -55,7 +55,7 @@ namespace MediaBrowser.Model.Dlna
                 stream.DeviceProfileId = options.Profile.Id;
             }
 
-            return GetOptimalStream(streams, options.GetMaxBitrate());
+            return GetOptimalStream(streams, options.GetMaxBitrate(true));
         }
 
         public StreamInfo BuildVideoItem(VideoOptions options)
@@ -88,7 +88,7 @@ namespace MediaBrowser.Model.Dlna
                 stream.DeviceProfileId = options.Profile.Id;
             }
 
-            return GetOptimalStream(streams, options.GetMaxBitrate());
+            return GetOptimalStream(streams, options.GetMaxBitrate(false));
         }
 
         private StreamInfo GetOptimalStream(List<StreamInfo> streams, int? maxBitrate)
@@ -275,24 +275,32 @@ namespace MediaBrowser.Model.Dlna
                     playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
                 }
 
-                int configuredBitrate = options.AudioTranscodingBitrate ??
-                    (options.Context == EncodingContext.Static ? options.Profile.MusicSyncBitrate : options.Profile.MusicStreamingTranscodingBitrate) ??
+                int transcodingBitrate = options.AudioTranscodingBitrate ??
+                    options.Profile.MusicStreamingTranscodingBitrate ??
                     128000;
 
-                playlistItem.AudioBitrate = Math.Min(configuredBitrate, playlistItem.AudioBitrate ?? configuredBitrate);
+                int? configuredBitrate = options.GetMaxBitrate(true);
+
+                if (configuredBitrate.HasValue)
+                {
+                    transcodingBitrate = Math.Min(configuredBitrate.Value, transcodingBitrate);
+                }
+
+                playlistItem.AudioBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate);
+
             }
 
             return playlistItem;
         }
 
-        private int? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options)
+        private int? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options, bool isAudio)
         {
             if (item.Protocol == MediaProtocol.File)
             {
                 return options.Profile.MaxStaticBitrate;
             }
 
-            return options.GetMaxBitrate();
+            return options.GetMaxBitrate(isAudio);
         }
 
         private List<PlayMethod> GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options)
@@ -312,7 +320,7 @@ namespace MediaBrowser.Model.Dlna
             if (directPlayProfile != null)
             {
                 // While options takes the network and other factors into account. Only applies to direct stream
-                if (item.SupportsDirectStream && IsAudioEligibleForDirectPlay(item, options.GetMaxBitrate()) && options.EnableDirectStream)
+                if (item.SupportsDirectStream && IsAudioEligibleForDirectPlay(item, options.GetMaxBitrate(true)) && options.EnableDirectStream)
                 {
                     playMethods.Add(PlayMethod.DirectStream);
                 }
@@ -320,7 +328,7 @@ namespace MediaBrowser.Model.Dlna
                 // The profile describes what the device supports
                 // If device requirements are satisfied then allow both direct stream and direct play
                 if (item.SupportsDirectPlay &&
-                    IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options)) && options.EnableDirectPlay)
+                    IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true)) && options.EnableDirectPlay)
                 {
                     playMethods.Add(PlayMethod.DirectPlay);
                 }
@@ -403,8 +411,8 @@ namespace MediaBrowser.Model.Dlna
             MediaStream videoStream = item.VideoStream;
 
             // TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough
-            bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options, PlayMethod.DirectPlay));
-            bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options, PlayMethod.DirectStream));
+            bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true), subtitleStream, options, PlayMethod.DirectPlay));
+            bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || IsEligibleForDirectPlay(item, options.GetMaxBitrate(false), subtitleStream, options, PlayMethod.DirectStream));
 
             _logger.Info("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
                 options.Profile.Name ?? "Unknown Profile",
@@ -469,7 +477,6 @@ namespace MediaBrowser.Model.Dlna
 
                 playlistItem.VideoCodec = transcodingProfile.VideoCodec;
                 playlistItem.CopyTimestamps = transcodingProfile.CopyTimestamps;
-                playlistItem.ForceLiveStream = transcodingProfile.ForceLiveStream;
                 playlistItem.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
 
                 if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels))
@@ -570,10 +577,10 @@ namespace MediaBrowser.Model.Dlna
                     playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
                 }
 
-                int audioBitrate = GetAudioBitrate(playlistItem.SubProtocol, options.GetMaxBitrate(), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream);
+                int audioBitrate = GetAudioBitrate(playlistItem.SubProtocol, options.GetMaxBitrate(false), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream);
                 playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
 
-                int? maxBitrateSetting = options.GetMaxBitrate();
+                int? maxBitrateSetting = options.GetMaxBitrate(false);
                 // Honor max rate
                 if (maxBitrateSetting.HasValue)
                 {
@@ -595,19 +602,16 @@ namespace MediaBrowser.Model.Dlna
 
         private int GetAudioBitrate(string subProtocol, int? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream)
         {
-            var defaultBitrate = 128000;
-            if (StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3"))
-            {
-                defaultBitrate = 192000;
-            }
-            if (!string.IsNullOrEmpty(targetAudioCodec) && audioStream != null && StringHelper.EqualsIgnoreCase(audioStream.Codec, targetAudioCodec))
+            var defaultBitrate = audioStream.BitRate ?? 192000;
+            // Reduce the bitrate if we're downmixing
+            if (targetAudioChannels.HasValue && audioStream != null && audioStream.Channels.HasValue && targetAudioChannels.Value < audioStream.Channels.Value)
             {
-                defaultBitrate = audioStream.BitRate ?? defaultBitrate;
+                defaultBitrate = StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3") ? 192000 : 128000;
             }
 
             if (targetAudioChannels.HasValue)
             {
-                if (targetAudioChannels.Value >= 5 && (maxTotalBitrate ?? 0) >= 1500000)
+                if (targetAudioChannels.Value >= 5 && (maxTotalBitrate ?? 0) >= 1200000)
                 {
                     if (StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3"))
                     {

+ 1 - 3
MediaBrowser.Model/Dlna/StreamInfo.cs

@@ -36,7 +36,6 @@ namespace MediaBrowser.Model.Dlna
         public string VideoProfile { get; set; }
 
         public bool CopyTimestamps { get; set; }
-        public bool ForceLiveStream { get; set; }
         public bool EnableSubtitlesInManifest { get; set; }
         public string[] AudioCodecs { get; set; }
 
@@ -216,7 +215,7 @@ namespace MediaBrowser.Model.Dlna
             list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty));
             list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty));
 
-            if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls") && !item.ForceLiveStream)
+            if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls"))
             {
                 list.Add(new NameValuePair("StartTimeTicks", string.Empty));
             }
@@ -246,7 +245,6 @@ namespace MediaBrowser.Model.Dlna
             }
 
             list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString().ToLower()));
-            list.Add(new NameValuePair("ForceLiveStream", item.ForceLiveStream.ToString().ToLower()));
             list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty));
 
             list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.TranscodingMaxAudioChannels.Value) : string.Empty));

+ 0 - 3
MediaBrowser.Model/Dlna/TranscodingProfile.cs

@@ -35,9 +35,6 @@ namespace MediaBrowser.Model.Dlna
         [XmlAttribute("context")]
         public EncodingContext Context { get; set; }
 
-        [XmlAttribute("forceLiveStream")]
-        public bool ForceLiveStream { get; set; }
-
         [XmlAttribute("enableSubtitlesInManifest")]
         public bool EnableSubtitlesInManifest { get; set; }
 

+ 2 - 1
MediaBrowser.Model/Entities/MetadataProviders.cs

@@ -39,6 +39,7 @@ namespace MediaBrowser.Model.Entities
         TvRage = 15,
         AudioDbArtist = 16,
         AudioDbAlbum = 17,
-        MusicBrainzTrack = 18
+        MusicBrainzTrack = 18,
+        TvMaze = 19
     }
 }

+ 6 - 0
MediaBrowser.Model/FileOrganization/FileOrganizationResult.cs

@@ -95,6 +95,12 @@ namespace MediaBrowser.Model.FileOrganization
         /// <value>The size of the file.</value>
         public long FileSize { get; set; }
 
+        /// <summary>
+        /// Indicates if the item is currently being processed.
+        /// </summary>
+        /// <remarks>Runtime property not persisted to the store.</remarks>
+        public bool IsInProgress { get; set; }
+
         public FileOrganizationResult()
         {
             DuplicatePaths = new List<string>();

+ 1 - 1
MediaBrowser.Model/Net/MimeTypes.cs

@@ -241,7 +241,7 @@ namespace MediaBrowser.Model.Net
 			}
 			if (StringHelper.EqualsIgnoreCase(ext, ".opus"))
 			{
-				return "audio/opus";
+				return "audio/ogg";
 			}
 
             // Playlists

+ 0 - 4
MediaBrowser.Model/Querying/ItemFilter.cs

@@ -27,10 +27,6 @@ namespace MediaBrowser.Model.Querying
         /// </summary>
         IsFavorite = 5,
         /// <summary>
-        /// The is recently added
-        /// </summary>
-        IsRecentlyAdded = 6,
-        /// <summary>
         /// The item is resumable
         /// </summary>
         IsResumable = 7,

+ 6 - 3
MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs

@@ -167,10 +167,13 @@ namespace MediaBrowser.Providers.MediaInfo
 
         public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
         {
-            var file = directoryService.GetFile(item.Path);
-            if (file != null && file.LastWriteTimeUtc != item.DateModified)
+            if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
             {
-                return true;
+                var file = directoryService.GetFile(item.Path);
+                if (file != null && file.LastWriteTimeUtc != item.DateModified)
+                {
+                    return true;
+                }
             }
 
             return false;

+ 1 - 1
MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs

@@ -171,7 +171,7 @@ namespace MediaBrowser.Providers.MediaInfo
 
         public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
         {
-            if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path))
+            if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
             {
                 var file = directoryService.GetFile(item.Path);
                 if (file != null && file.LastWriteTimeUtc != item.DateModified)

+ 1 - 1
MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs

@@ -194,7 +194,7 @@ namespace MediaBrowser.Providers.MediaInfo
 
         public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
         {
-            if (item.EnableRefreshOnDateModifiedChange)
+            if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
             {
                 var file = directoryService.GetFile(item.Path);
                 if (file != null && file.LastWriteTimeUtc != item.DateModified)

+ 6 - 3
MediaBrowser.Providers/Photos/PhotoProvider.cs

@@ -154,10 +154,13 @@ namespace MediaBrowser.Providers.Photos
 
         public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
         {
-            var file = directoryService.GetFile(item.Path);
-            if (file != null && file.LastWriteTimeUtc != item.DateModified)
+            if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
             {
-                return true;
+                var file = directoryService.GetFile(item.Path);
+                if (file != null && file.LastWriteTimeUtc != item.DateModified)
+                {
+                    return true;
+                }
             }
 
             return false;

+ 7 - 10
MediaBrowser.Server.Implementations/EntryPoints/RecordingNotifier.cs

@@ -54,18 +54,15 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
 
         private async void SendMessage(string name, TimerEventInfo info)
         {
-            var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).ToList();
+            var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).Select(i => i.Id.ToString("N")).ToList();
 
-            foreach (var user in users)
+            try
             {
-                try
-                {
-                    await _sessionManager.SendMessageToUserSessions<TimerEventInfo>(user.Id.ToString("N"), name, info, CancellationToken.None);
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error sending message", ex);
-                }
+                await _sessionManager.SendMessageToUserSessions<TimerEventInfo>(users, name, info, CancellationToken.None);
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error sending message", ex);
             }
         }
 

+ 2 - 1
MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs

@@ -11,6 +11,7 @@ using MediaBrowser.Controller.Sync;
 using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Sync;
 using System;
+using System.Collections.Generic;
 using System.Threading;
 
 namespace MediaBrowser.Server.Implementations.EntryPoints
@@ -164,7 +165,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
 
         private async void SendMessageToUserSession<T>(User user, string name, T data)
         {
-            await _sessionManager.SendMessageToUserSessions(user.Id.ToString("N"), name, data, CancellationToken.None);
+            await _sessionManager.SendMessageToUserSessions(new List<string> { user.Id.ToString("N") }, name, data, CancellationToken.None);
         }
 
         /// <summary>

+ 16 - 0
MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs

@@ -272,6 +272,18 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
             var originalExtractedSeriesString = result.ExtractedName;
 
+            bool isNew = string.IsNullOrWhiteSpace(result.Id);
+
+            if (isNew)
+            {
+                await _organizationService.SaveResult(result, cancellationToken);
+            }
+
+            if (!_organizationService.AddToInProgressList(result, isNew))
+            {
+                throw new Exception("File is currently processed otherwise. Please try again later.");
+            }
+            
             try
             {
             // Proceed to sort the file
@@ -363,6 +375,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 _logger.Warn(ex.Message);
                 return;
             }
+            finally
+            {
+                _organizationService.RemoveFromInprogressList(result);
+            }
 
             if (rememberCorrection)
             {

+ 68 - 0
MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs

@@ -0,0 +1,68 @@
+using MediaBrowser.Controller.FileOrganization;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.FileOrganization;
+using MediaBrowser.Controller.Session;
+using System.Threading;
+
+namespace MediaBrowser.Server.Implementations.FileOrganization
+{
+    /// <summary>
+    /// Class SessionInfoWebSocketListener
+    /// </summary>
+    class FileOrganizationNotifier : IServerEntryPoint
+    {
+        private readonly IFileOrganizationService _organizationService;
+        private readonly ISessionManager _sessionManager;
+
+        public FileOrganizationNotifier(ILogger logger, IFileOrganizationService organizationService, ISessionManager sessionManager)
+        {
+            _organizationService = organizationService;
+            _sessionManager = sessionManager;
+        }
+
+        public void Run()
+        {
+            _organizationService.ItemAdded += _organizationService_ItemAdded;
+            _organizationService.ItemRemoved += _organizationService_ItemRemoved;
+            _organizationService.ItemUpdated += _organizationService_ItemUpdated;
+            _organizationService.LogReset += _organizationService_LogReset;
+        }
+
+        private void _organizationService_LogReset(object sender, EventArgs e)
+        {
+            _sessionManager.SendMessageToAdminSessions("AutoOrganizeUpdate", (FileOrganizationResult)null, CancellationToken.None);
+        }
+
+        private void _organizationService_ItemUpdated(object sender, GenericEventArgs<FileOrganizationResult> e)
+        {
+            _sessionManager.SendMessageToAdminSessions("AutoOrganizeUpdate", e.Argument, CancellationToken.None);
+        }
+
+        private void _organizationService_ItemRemoved(object sender, GenericEventArgs<FileOrganizationResult> e)
+        {
+            _sessionManager.SendMessageToAdminSessions("AutoOrganizeUpdate", (FileOrganizationResult)null, CancellationToken.None);
+        }
+
+        private void _organizationService_ItemAdded(object sender, GenericEventArgs<FileOrganizationResult> e)
+        {
+            _sessionManager.SendMessageToAdminSessions("AutoOrganizeUpdate", (FileOrganizationResult)null, CancellationToken.None);
+        }
+
+        public void Dispose()
+        {
+            _organizationService.ItemAdded -= _organizationService_ItemAdded;
+            _organizationService.ItemRemoved -= _organizationService_ItemRemoved;
+            _organizationService.ItemUpdated -= _organizationService_ItemUpdated;
+            _organizationService.LogReset -= _organizationService_LogReset;
+        }
+
+
+    }
+}

+ 94 - 6
MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs

@@ -3,16 +3,21 @@ using MediaBrowser.Common.ScheduledTasks;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.FileOrganization;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.FileOrganization;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Querying;
 using System;
+using System.Collections.Concurrent;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Common.Events;
 
 namespace MediaBrowser.Server.Implementations.FileOrganization
 {
@@ -26,6 +31,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         private readonly IServerConfigurationManager _config;
         private readonly IFileSystem _fileSystem;
         private readonly IProviderManager _providerManager;
+        private readonly ConcurrentDictionary<string, bool> _inProgressItemIds = new ConcurrentDictionary<string, bool>();
+
+        public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemAdded;
+        public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemUpdated;
+        public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemRemoved;
+        public event EventHandler LogReset;
 
         public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager)
         {
@@ -58,12 +69,26 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
         public QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query)
         {
-            return _repo.GetResults(query);
+            var results = _repo.GetResults(query);
+
+            foreach (var result in results.Items)
+            {
+                result.IsInProgress = _inProgressItemIds.ContainsKey(result.Id);
+            }
+
+            return results;
         }
 
         public FileOrganizationResult GetResult(string id)
         {
-            return _repo.GetResult(id);
+            var result = _repo.GetResult(id);
+
+            if (result != null)
+            {
+                result.IsInProgress = _inProgressItemIds.ContainsKey(result.Id);
+            }
+
+            return result;
         }
 
         public FileOrganizationResult GetResultBySourcePath(string path)
@@ -78,11 +103,17 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             return GetResult(id);
         }
 
-        public Task DeleteOriginalFile(string resultId)
+        public async Task DeleteOriginalFile(string resultId)
         {
             var result = _repo.GetResult(resultId);
 
             _logger.Info("Requested to delete {0}", result.OriginalPath);
+
+            if (!AddToInProgressList(result, false))
+            {
+                throw new Exception("Path is currently processed otherwise. Please try again later.");
+            }
+
             try
             {
                 _fileSystem.DeleteFile(result.OriginalPath);
@@ -91,8 +122,14 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             {
                 _logger.ErrorException("Error deleting {0}", ex, result.OriginalPath);
             }
+            finally
+            {
+                RemoveFromInprogressList(result);
+            }
 
-            return _repo.Delete(resultId);
+            await _repo.Delete(resultId);
+
+            EventHelper.FireEventIfNotNull(ItemRemoved, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
         }
 
         private AutoOrganizeOptions GetAutoOrganizeOptions()
@@ -121,9 +158,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             }
         }
 
-        public Task ClearLog()
+        public async Task ClearLog()
         {
-            return _repo.DeleteAll();
+            await _repo.DeleteAll();
+            EventHelper.FireEventIfNotNull(LogReset, this, EventArgs.Empty, _logger);
         }
 
         public async Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request)
@@ -189,5 +227,55 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 _config.SaveAutoOrganizeOptions(options);
             }
         }
+
+        /// <summary>
+        /// Attempts to add a an item to the list of currently processed items.
+        /// </summary>
+        /// <param name="result">The result item.</param>
+        /// <param name="isNewItem">Passing true will notify the client to reload all items, otherwise only a single item will be refreshed.</param>
+        /// <returns>True if the item was added, False if the item is already contained in the list.</returns>
+        public bool AddToInProgressList(FileOrganizationResult result, bool isNewItem)
+        {
+            if (string.IsNullOrWhiteSpace(result.Id))
+            {
+                result.Id = result.OriginalPath.GetMD5().ToString("N");
+            }
+
+            if (!_inProgressItemIds.TryAdd(result.Id, false))
+            {
+                return false;
+            }
+
+            result.IsInProgress = true;
+
+            if (isNewItem)
+            {
+                EventHelper.FireEventIfNotNull(ItemAdded, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
+            }
+            else
+            {
+                EventHelper.FireEventIfNotNull(ItemUpdated, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// Removes an item from the list of currently processed items.
+        /// </summary>
+        /// <param name="result">The result item.</param>
+        /// <returns>True if the item was removed, False if the item was not contained in the list.</returns>
+        public bool RemoveFromInprogressList(FileOrganizationResult result)
+        {
+            bool itemValue;
+            var retval = _inProgressItemIds.TryRemove(result.Id, out itemValue);
+
+            result.IsInProgress = false;
+
+            EventHelper.FireEventIfNotNull(ItemUpdated, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
+
+            return retval;
+        }
+
     }
 }

+ 18 - 2
MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -428,8 +428,24 @@ namespace MediaBrowser.Server.Implementations.HttpServer
 
             if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) ||
                 string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) ||
-                localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1 ||
-                localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1)
+                localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                httpRes.StatusCode = 200;
+                httpRes.ContentType = "text/html";
+                var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase)
+                    .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase);
+
+                if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
+                {
+                    httpRes.Write("<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + newUrl + "\">" + newUrl + "</a></body></html>");
+
+                    httpRes.Close();
+                    return;
+                }
+            }
+
+            if (localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1 && 
+                localPath.IndexOf("web/dashboard", StringComparison.OrdinalIgnoreCase) == -1)
             {
                 httpRes.StatusCode = 200;
                 httpRes.ContentType = "text/html";

+ 21 - 19
MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs

@@ -172,27 +172,29 @@ namespace MediaBrowser.Server.Implementations.IO
             }
         }
 
-        public void Start()
+        private bool IsLibraryMonitorEnabaled(BaseItem item)
         {
-            if (EnableLibraryMonitor)
+            var options = LibraryManager.GetLibraryOptions(item);
+
+            if (options != null && options.SchemaVersion >= 1)
             {
-                StartInternal();
+                return options.EnableRealtimeMonitor;
             }
+
+            return EnableLibraryMonitor;
         }
 
-        /// <summary>
-        /// Starts this instance.
-        /// </summary>
-        private void StartInternal()
+        public void Start()
         {
             LibraryManager.ItemAdded += LibraryManager_ItemAdded;
             LibraryManager.ItemRemoved += LibraryManager_ItemRemoved;
 
-            var pathsToWatch = new List<string> { LibraryManager.RootFolder.Path };
+            var pathsToWatch = new List<string> { };
 
             var paths = LibraryManager
                 .RootFolder
                 .Children
+                .Where(IsLibraryMonitorEnabaled)
                 .OfType<Folder>()
                 .SelectMany(f => f.PhysicalLocations)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
@@ -213,6 +215,14 @@ namespace MediaBrowser.Server.Implementations.IO
             }
         }
 
+        private void StartWatching(BaseItem item)
+        {
+            if (IsLibraryMonitorEnabaled(item))
+            {
+                StartWatchingPath(item.Path);
+            }
+        }
+
         /// <summary>
         /// Handles the ItemRemoved event of the LibraryManager control.
         /// </summary>
@@ -235,7 +245,7 @@ namespace MediaBrowser.Server.Implementations.IO
         {
             if (e.Item.GetParent() is AggregateFolder)
             {
-                StartWatchingPath(e.Item.Path);
+                StartWatching(e.Item);
             }
         }
 
@@ -382,14 +392,6 @@ namespace MediaBrowser.Server.Implementations.IO
             Logger.ErrorException("Error in Directory watcher for: " + dw.Path, ex);
 
             DisposeWatcher(dw);
-
-            if (ConfigurationManager.Configuration.EnableLibraryMonitor == AutoOnOff.Auto)
-            {
-                Logger.Info("Disabling realtime monitor to prevent future instability");
-
-                ConfigurationManager.Configuration.EnableLibraryMonitor = AutoOnOff.Disabled;
-                Stop();
-            }
         }
 
         /// <summary>
@@ -420,8 +422,8 @@ namespace MediaBrowser.Server.Implementations.IO
 
             var filename = Path.GetFileName(path);
 
-            var monitorPath = !string.IsNullOrEmpty(filename) && 
-                !_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) && 
+            var monitorPath = !string.IsNullOrEmpty(filename) &&
+                !_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) &&
                 !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase);
 
             // Ignore certain files

+ 0 - 6
MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -203,12 +203,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
         /// <returns>Video.</returns>
         protected override Video Resolve(ItemResolveArgs args)
         {
-            if (args.Path != null && args.Path.IndexOf("disney", StringComparison.OrdinalIgnoreCase) != -1)
-            {
-                var b = true;
-                var a = b;
-            }
-
             var collectionType = args.GetCollectionType();
 
             if (IsInvalid(args.Parent, collectionType))

+ 2 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -149,6 +149,7 @@
     <Compile Include="EntryPoints\UsageReporter.cs" />
     <Compile Include="FileOrganization\EpisodeFileOrganizer.cs" />
     <Compile Include="FileOrganization\Extensions.cs" />
+    <Compile Include="FileOrganization\FileOrganizationNotifier.cs" />
     <Compile Include="FileOrganization\FileOrganizationService.cs" />
     <Compile Include="FileOrganization\NameUtils.cs" />
     <Compile Include="FileOrganization\TvFolderOrganizer.cs" />
@@ -271,6 +272,7 @@
     <Compile Include="Sorting\StartDateComparer.cs" />
     <Compile Include="Sync\SyncHelper.cs" />
     <Compile Include="Sync\SyncJobOptions.cs" />
+    <Compile Include="Sync\SyncNotificationEntryPoint.cs" />
     <Compile Include="UserViews\CollectionFolderImageProvider.cs" />
     <Compile Include="UserViews\DynamicImageProvider.cs" />
     <Compile Include="News\NewsEntryPoint.cs" />

+ 1 - 1
MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs

@@ -123,7 +123,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
                 {
                     if (extractImages)
                     {
-                        if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso || video.VideoType == VideoType.BluRay)
+                        if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso || video.VideoType == VideoType.BluRay || video.VideoType == VideoType.Dvd)
                         {
                             continue;
                         }

+ 1 - 2
MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs

@@ -313,8 +313,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
                     if (Folder.IsPathOffline(path))
                     {
-                        libraryItem.IsOffline = true;
-                        await libraryItem.UpdateToRepository(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
+                        await libraryItem.UpdateIsOffline(true).ConfigureAwait(false);
                         continue;
                     }
 

+ 6 - 3
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -211,7 +211,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
             _connection.AddColumn(Logger, "TypedBaseItems", "ProductionYear", "INT");
             _connection.AddColumn(Logger, "TypedBaseItems", "ParentId", "GUID");
             _connection.AddColumn(Logger, "TypedBaseItems", "Genres", "Text");
-            _connection.AddColumn(Logger, "TypedBaseItems", "ParentalRatingValue", "INT");
             _connection.AddColumn(Logger, "TypedBaseItems", "SchemaVersion", "INT");
             _connection.AddColumn(Logger, "TypedBaseItems", "SortName", "Text");
             _connection.AddColumn(Logger, "TypedBaseItems", "RunTimeTicks", "BIGINT");
@@ -488,7 +487,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 "ProductionYear",
                 "ParentId",
                 "Genres",
-                "ParentalRatingValue",
                 "InheritedParentalRatingValue",
                 "SchemaVersion",
                 "SortName",
@@ -795,7 +793,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
                     }
 
                     _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Genres.ToArray());
-                    _saveItemCommand.GetParameter(index++).Value = item.GetParentalRatingValue() ?? 0;
                     _saveItemCommand.GetParameter(index++).Value = item.GetInheritedParentalRatingValue() ?? 0;
 
                     _saveItemCommand.GetParameter(index++).Value = LatestSchemaVersion;
@@ -4286,6 +4283,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
             var index = 0;
             foreach (var image in images)
             {
+                if (string.IsNullOrWhiteSpace(image.Path))
+                {
+                    // Invalid
+                    continue;
+                }
+
                 _saveImagesCommand.GetParameter(0).Value = itemId;
                 _saveImagesCommand.GetParameter(1).Value = image.Type;
                 _saveImagesCommand.GetParameter(2).Value = image.Path;

+ 9 - 2
MediaBrowser.Server.Implementations/Session/SessionManager.cs

@@ -1869,10 +1869,17 @@ namespace MediaBrowser.Server.Implementations.Session
             return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null);
         }
 
-        public Task SendMessageToUserSessions<T>(string userId, string name, T data,
+        public Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken)
+        {
+            var adminUserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id.ToString("N")).ToList();
+
+            return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken);
+        }
+
+        public Task SendMessageToUserSessions<T>(List<string> userIds, string name, T data,
             CancellationToken cancellationToken)
         {
-            var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null && i.ContainsUser(userId)).ToList();
+            var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null && userIds.Any(i.ContainsUser)).ToList();
 
             var tasks = sessions.Select(session => Task.Run(async () =>
             {

+ 5 - 0
MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs

@@ -85,6 +85,11 @@ namespace MediaBrowser.Server.Implementations.Sync
                 {
                     Name = "Low",
                     Id = "low"
+                },
+                new SyncQualityOption
+                {
+                    Name = "Custom",
+                    Id = "custom"
                 }
             };
         }

+ 48 - 0
MediaBrowser.Server.Implementations/Sync/SyncNotificationEntryPoint.cs

@@ -0,0 +1,48 @@
+using System.Threading;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Controller.Sync;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Sync;
+
+namespace MediaBrowser.Server.Implementations.Sync
+{
+    public class SyncNotificationEntryPoint : IServerEntryPoint
+    {
+        private readonly ISessionManager _sessionManager;
+        private readonly ISyncManager _syncManager;
+
+        public SyncNotificationEntryPoint(ISyncManager syncManager, ISessionManager sessionManager)
+        {
+            _syncManager = syncManager;
+            _sessionManager = sessionManager;
+        }
+
+        public void Run()
+        {
+            _syncManager.SyncJobItemUpdated += _syncManager_SyncJobItemUpdated;
+        }
+
+        private async void _syncManager_SyncJobItemUpdated(object sender, GenericEventArgs<SyncJobItem> e)
+        {
+            var item = e.Argument;
+
+            if (item.Status == SyncJobItemStatus.ReadyToTransfer)
+            {
+                try
+                {
+                    await _sessionManager.SendMessageToUserDeviceSessions(item.TargetId, "SyncJobItemReady", item, CancellationToken.None).ConfigureAwait(false);
+                }
+                catch
+                {
+
+                }
+            }
+        }
+
+        public void Dispose()
+        {
+            _syncManager.SyncJobItemUpdated -= _syncManager_SyncJobItemUpdated;
+        }
+    }
+}

+ 71 - 323
MediaBrowser.Server.Mac/Emby.Server.Mac.csproj

@@ -390,6 +390,9 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\autoorganizetv.html">
       <Link>Resources\dashboard-ui\autoorganizetv.html</Link>
     </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\camerauploadsettings.html">
+      <Link>Resources\dashboard-ui\camerauploadsettings.html</Link>
+    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\channelitems.html">
       <Link>Resources\dashboard-ui\channelitems.html</Link>
     </BundleResource>
@@ -1128,9 +1131,6 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\multidownload.js">
       <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\multidownload.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\objectassign.js">
-      <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\objectassign.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playmenu.js">
       <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playmenu.js</Link>
     </BundleResource>
@@ -1155,6 +1155,9 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\shortcuts.js">
       <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\shortcuts.js</Link>
     </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\thememediaplayer.js">
+      <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\thememediaplayer.js</Link>
+    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\visibleinviewport.js">
       <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\visibleinviewport.js</Link>
     </BundleResource>
@@ -1566,6 +1569,15 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playlisteditor\playlisteditor.js">
       <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playlisteditor\playlisteditor.js</Link>
     </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\polyfills\array.js">
+      <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\polyfills\array.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\polyfills\bind.js">
+      <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\polyfills\bind.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\polyfills\objectassign.js">
+      <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\polyfills\objectassign.js</Link>
+    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\prompt\nativeprompt.js">
       <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\prompt\nativeprompt.js</Link>
     </BundleResource>
@@ -1773,6 +1785,9 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\subtitleeditor\subtitleeditor.template.html">
       <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\subtitleeditor\subtitleeditor.template.html</Link>
     </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\sync\sync.js">
+      <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\sync\sync.js</Link>
+    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\toast\toast.css">
       <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\toast\toast.css</Link>
     </BundleResource>
@@ -1860,36 +1875,6 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fetch\fetch.js">
       <Link>Resources\dashboard-ui\bower_components\fetch\fetch.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\.bower.json">
-      <Link>Resources\dashboard-ui\bower_components\fingerprintjs2\.bower.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\CONTRIBUTING.md">
-      <Link>Resources\dashboard-ui\bower_components\fingerprintjs2\CONTRIBUTING.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\FAQ.md">
-      <Link>Resources\dashboard-ui\bower_components\fingerprintjs2\FAQ.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\README.md">
-      <Link>Resources\dashboard-ui\bower_components\fingerprintjs2\README.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\bower.json">
-      <Link>Resources\dashboard-ui\bower_components\fingerprintjs2\bower.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\fingerprint2.js">
-      <Link>Resources\dashboard-ui\bower_components\fingerprintjs2\fingerprint2.js</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\gulpfile.js">
-      <Link>Resources\dashboard-ui\bower_components\fingerprintjs2\gulpfile.js</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\index.html">
-      <Link>Resources\dashboard-ui\bower_components\fingerprintjs2\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\package.json">
-      <Link>Resources\dashboard-ui\bower_components\fingerprintjs2\package.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\dist\fingerprint2.min.js">
-      <Link>Resources\dashboard-ui\bower_components\fingerprintjs2\dist\fingerprint2.min.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\font-roboto\.bower.json">
       <Link>Resources\dashboard-ui\bower_components\font-roboto\.bower.json</Link>
     </BundleResource>
@@ -2094,45 +2079,6 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\test\index.html">
       <Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\test\index.html</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\.bower.json">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\.bower.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\.gitignore">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\.gitignore</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\.travis.yml">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\.travis.yml</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\CONTRIBUTING.md">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\CONTRIBUTING.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\README.md">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\README.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\bower.json">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\bower.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\hero.svg">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\hero.svg</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\index.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\iron-autogrow-textarea.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\iron-autogrow-textarea.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\.github\ISSUE_TEMPLATE.md">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\.github\ISSUE_TEMPLATE.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\demo\index.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\demo\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\test\basic.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\test\basic.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\test\index.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\test\index.html</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\.bower.json">
       <Link>Resources\dashboard-ui\bower_components\iron-behaviors\.bower.json</Link>
     </BundleResource>
@@ -2502,51 +2448,6 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\test\iron-iconset-svg.html">
       <Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\test\iron-iconset-svg.html</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\.bower.json">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\.bower.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\.gitignore">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\.gitignore</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\.travis.yml">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\.travis.yml</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\CONTRIBUTING.md">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\CONTRIBUTING.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\README.md">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\README.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\bower.json">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\bower.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\hero.svg">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\hero.svg</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\index.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\iron-input.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\iron-input.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\.github\ISSUE_TEMPLATE.md">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\.github\ISSUE_TEMPLATE.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\demo\index.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\demo\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\test\disabled-input.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\test\disabled-input.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\test\index.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\test\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\test\iron-input.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\test\iron-input.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\test\letters-only.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-input\test\letters-only.html</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\.bower.json">
       <Link>Resources\dashboard-ui\bower_components\iron-meta\.bower.json</Link>
     </BundleResource>
@@ -2574,6 +2475,9 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\iron-meta.html">
       <Link>Resources\dashboard-ui\bower_components\iron-meta\iron-meta.html</Link>
     </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\.github\ISSUE_TEMPLATE.md">
+      <Link>Resources\dashboard-ui\bower_components\iron-meta\.github\ISSUE_TEMPLATE.md</Link>
+    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\demo\index.html">
       <Link>Resources\dashboard-ui\bower_components\iron-meta\demo\index.html</Link>
     </BundleResource>
@@ -2586,45 +2490,6 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\test\iron-meta.html">
       <Link>Resources\dashboard-ui\bower_components\iron-meta\test\iron-meta.html</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\.bower.json">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\.bower.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\.gitignore">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\.gitignore</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\.travis.yml">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\.travis.yml</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\CONTRIBUTING.md">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\CONTRIBUTING.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\README.md">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\README.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\bower.json">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\bower.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\index.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\iron-range-behavior.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\iron-range-behavior.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\.github\ISSUE_TEMPLATE.md">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\.github\ISSUE_TEMPLATE.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\demo\index.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\demo\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\test\basic.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\test\basic.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\test\index.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\test\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\test\x-progressbar.html">
-      <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\test\x-progressbar.html</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\.bower.json">
       <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\.bower.json</Link>
     </BundleResource>
@@ -3252,126 +3117,6 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\test\index.html">
       <Link>Resources\dashboard-ui\bower_components\paper-icon-button\test\index.html</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\.bower.json">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\.bower.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\.gitignore">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\.gitignore</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\.travis.yml">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\.travis.yml</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\CONTRIBUTING.md">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\CONTRIBUTING.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\README.md">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\README.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\all-imports.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\all-imports.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\bower.json">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\bower.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\hero.svg">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\hero.svg</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\index.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-addon-behavior.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-addon-behavior.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-behavior.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-behavior.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-char-counter.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-char-counter.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-container.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-container.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-error.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-error.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\paper-input.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-textarea.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\paper-textarea.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\.github\ISSUE_TEMPLATE.md">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\.github\ISSUE_TEMPLATE.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\demo\index.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\demo\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\demo\ssn-input.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\demo\ssn-input.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\demo\ssn-validator.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\demo\ssn-validator.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\index.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\test\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\letters-only.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\test\letters-only.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-input-char-counter.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-input-char-counter.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-input-container.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-input-container.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-input-error.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-input-error.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-input.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-input.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-textarea.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-textarea.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\.bower.json">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\.bower.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\.gitignore">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\.gitignore</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\.travis.yml">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\.travis.yml</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\CONTRIBUTING.md">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\CONTRIBUTING.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\README.md">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\README.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\bower.json">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\bower.json</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\hero.svg">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\hero.svg</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\index.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\paper-progress.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\paper-progress.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\.github\ISSUE_TEMPLATE.md">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\.github\ISSUE_TEMPLATE.md</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\demo\index.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\demo\index.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\test\basic.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\test\basic.html</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\test\index.html">
-      <Link>Resources\dashboard-ui\bower_components\paper-progress\test\index.html</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\.bower.json">
       <Link>Resources\dashboard-ui\bower_components\paper-ripple\.bower.json</Link>
     </BundleResource>
@@ -3729,6 +3474,9 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\tvproviders\xmltv.template.html">
       <Link>Resources\dashboard-ui\components\tvproviders\xmltv.template.html</Link>
     </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\autoorganizetable.css">
+      <Link>Resources\dashboard-ui\css\autoorganizetable.css</Link>
+    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\chromecast.css">
       <Link>Resources\dashboard-ui\css\chromecast.css</Link>
     </BundleResource>
@@ -3801,6 +3549,9 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\rotten.png">
       <Link>Resources\dashboard-ui\css\images\rotten.png</Link>
     </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\throbber.gif">
+      <Link>Resources\dashboard-ui\css\images\throbber.gif</Link>
+    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\userflyoutdefault.png">
       <Link>Resources\dashboard-ui\css\images\userflyoutdefault.png</Link>
     </BundleResource>
@@ -3987,6 +3738,48 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\userdata\password.png">
       <Link>Resources\dashboard-ui\css\images\userdata\password.png</Link>
     </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\aboutpage.js">
+      <Link>Resources\dashboard-ui\dashboard\aboutpage.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\autoorganizelog.js">
+      <Link>Resources\dashboard-ui\dashboard\autoorganizelog.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\autoorganizesmart.js">
+      <Link>Resources\dashboard-ui\dashboard\autoorganizesmart.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\autoorganizetv.js">
+      <Link>Resources\dashboard-ui\dashboard\autoorganizetv.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\cinemamodeconfiguration.js">
+      <Link>Resources\dashboard-ui\dashboard\cinemamodeconfiguration.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\dashboardgeneral.js">
+      <Link>Resources\dashboard-ui\dashboard\dashboardgeneral.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\dashboardhosting.js">
+      <Link>Resources\dashboard-ui\dashboard\dashboardhosting.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\devicesupload.js">
+      <Link>Resources\dashboard-ui\dashboard\devicesupload.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\librarydisplay.js">
+      <Link>Resources\dashboard-ui\dashboard\librarydisplay.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\librarysettings.js">
+      <Link>Resources\dashboard-ui\dashboard\librarysettings.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\livetvtunerprovider-satip.js">
+      <Link>Resources\dashboard-ui\dashboard\livetvtunerprovider-satip.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\logpage.js">
+      <Link>Resources\dashboard-ui\dashboard\logpage.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\wizardcomponents.js">
+      <Link>Resources\dashboard-ui\dashboard\wizardcomponents.js</Link>
+    </BundleResource>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\wizardfinishpage.js">
+      <Link>Resources\dashboard-ui\dashboard\wizardfinishpage.js</Link>
+    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\devices\android\android.css">
       <Link>Resources\dashboard-ui\devices\android\android.css</Link>
     </BundleResource>
@@ -4008,9 +3801,6 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\legacy\selectmenu.js">
       <Link>Resources\dashboard-ui\legacy\selectmenu.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\aboutpage.js">
-      <Link>Resources\dashboard-ui\scripts\aboutpage.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\addpluginpage.js">
       <Link>Resources\dashboard-ui\scripts\addpluginpage.js</Link>
     </BundleResource>
@@ -4020,14 +3810,8 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\autobackdrops.js">
       <Link>Resources\dashboard-ui\scripts\autobackdrops.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\autoorganizelog.js">
-      <Link>Resources\dashboard-ui\scripts\autoorganizelog.js</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\autoorganizesmart.js">
-      <Link>Resources\dashboard-ui\scripts\autoorganizesmart.js</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\autoorganizetv.js">
-      <Link>Resources\dashboard-ui\scripts\autoorganizetv.js</Link>
+    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\camerauploadsettings.js">
+      <Link>Resources\dashboard-ui\scripts\camerauploadsettings.js</Link>
     </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\channelitems.js">
       <Link>Resources\dashboard-ui\scripts\channelitems.js</Link>
@@ -4041,18 +3825,9 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\chromecast.js">
       <Link>Resources\dashboard-ui\scripts\chromecast.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\cinemamodeconfiguration.js">
-      <Link>Resources\dashboard-ui\scripts\cinemamodeconfiguration.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\connectlogin.js">
       <Link>Resources\dashboard-ui\scripts\connectlogin.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\dashboardgeneral.js">
-      <Link>Resources\dashboard-ui\scripts\dashboardgeneral.js</Link>
-    </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\dashboardhosting.js">
-      <Link>Resources\dashboard-ui\scripts\dashboardhosting.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\dashboardpage.js">
       <Link>Resources\dashboard-ui\scripts\dashboardpage.js</Link>
     </BundleResource>
@@ -4062,9 +3837,6 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\devices.js">
       <Link>Resources\dashboard-ui\scripts\devices.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\devicesupload.js">
-      <Link>Resources\dashboard-ui\scripts\devicesupload.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\dlnaprofile.js">
       <Link>Resources\dashboard-ui\scripts\dlnaprofile.js</Link>
     </BundleResource>
@@ -4140,18 +3912,12 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarybrowser.js">
       <Link>Resources\dashboard-ui\scripts\librarybrowser.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarydisplay.js">
-      <Link>Resources\dashboard-ui\scripts\librarydisplay.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarymenu.js">
       <Link>Resources\dashboard-ui\scripts\librarymenu.js</Link>
     </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarypathmapping.js">
       <Link>Resources\dashboard-ui\scripts\librarypathmapping.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarysettings.js">
-      <Link>Resources\dashboard-ui\scripts\librarysettings.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvchannel.js">
       <Link>Resources\dashboard-ui\scripts\livetvchannel.js</Link>
     </BundleResource>
@@ -4197,18 +3963,12 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvtunerprovider-m3u.js">
       <Link>Resources\dashboard-ui\scripts\livetvtunerprovider-m3u.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvtunerprovider-satip.js">
-      <Link>Resources\dashboard-ui\scripts\livetvtunerprovider-satip.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\localsync.js">
       <Link>Resources\dashboard-ui\scripts\localsync.js</Link>
     </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\loginpage.js">
       <Link>Resources\dashboard-ui\scripts\loginpage.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\logpage.js">
-      <Link>Resources\dashboard-ui\scripts\logpage.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mediacontroller.js">
       <Link>Resources\dashboard-ui\scripts\mediacontroller.js</Link>
     </BundleResource>
@@ -4371,9 +4131,6 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\supporterkeypage.js">
       <Link>Resources\dashboard-ui\scripts\supporterkeypage.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\sync.js">
-      <Link>Resources\dashboard-ui\scripts\sync.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\syncactivity.js">
       <Link>Resources\dashboard-ui\scripts\syncactivity.js</Link>
     </BundleResource>
@@ -4386,9 +4143,6 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\taskbutton.js">
       <Link>Resources\dashboard-ui\scripts\taskbutton.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\thememediaplayer.js">
-      <Link>Resources\dashboard-ui\scripts\thememediaplayer.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\tvgenres.js">
       <Link>Resources\dashboard-ui\scripts\tvgenres.js</Link>
     </BundleResource>
@@ -4431,15 +4185,9 @@
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardagreement.js">
       <Link>Resources\dashboard-ui\scripts\wizardagreement.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardcomponents.js">
-      <Link>Resources\dashboard-ui\scripts\wizardcomponents.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardcontroller.js">
       <Link>Resources\dashboard-ui\scripts\wizardcontroller.js</Link>
     </BundleResource>
-    <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardfinishpage.js">
-      <Link>Resources\dashboard-ui\scripts\wizardfinishpage.js</Link>
-    </BundleResource>
     <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardlivetvguide.js">
       <Link>Resources\dashboard-ui\scripts\wizardlivetvguide.js</Link>
     </BundleResource>

+ 4 - 25
MediaBrowser.Server.Mono/Native/BaseMonoApp.cs

@@ -132,7 +132,7 @@ namespace MediaBrowser.Server.Mono.Native
         {
             get
             {
-				return Environment.OperatingSystem != Startup.Common.OperatingSystem.Osx;
+                return Environment.OperatingSystem != Startup.Common.OperatingSystem.Osx;
             }
         }
 
@@ -187,7 +187,7 @@ namespace MediaBrowser.Server.Mono.Native
             {
                 info.SystemArchitecture = Architecture.X64;
             }
-            else 
+            else
             {
                 info.SystemArchitecture = Architecture.X86;
             }
@@ -273,32 +273,11 @@ namespace MediaBrowser.Server.Mono.Native
                     break;
             }
 
-            info.DownloadUrls = GetDownloadUrls(environment);
+            // No version available - user requirement
+            info.DownloadUrls = new string[] { };
 
             return info;
         }
-
-        private static string[] GetDownloadUrls(NativeEnvironment environment)
-        {
-            switch (environment.OperatingSystem)
-            {
-                case OperatingSystem.Linux:
-
-                    switch (environment.SystemArchitecture)
-                    {
-                        case Architecture.X64:
-                            return new[]
-                            {
-                                "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z"
-                            };
-                    }
-                    break;
-            }
-
-            // No version available 
-            return new string[] { };
-        }
-
     }
 
     public class NullPowerManagement : IPowerManagement

+ 1 - 2
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -385,8 +385,7 @@ namespace MediaBrowser.Server.Startup.Common
                 new OmdbEpisodeProviderMigration(ServerConfigurationManager),
                 new MovieDbEpisodeProviderMigration(ServerConfigurationManager),
                 new DbMigration(ServerConfigurationManager, TaskManager),
-                new FolderViewSettingMigration(ServerConfigurationManager, UserManager),
-                new CollectionGroupingMigration(ServerConfigurationManager, UserManager),
+                new UpdateLevelMigration(ServerConfigurationManager, this, HttpClient, JsonSerializer, _releaseAssetFilename),
                 new CollectionsViewMigration(ServerConfigurationManager, UserManager)
             };
 

+ 1 - 2
MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj

@@ -70,13 +70,12 @@
     <Compile Include="FFMpeg\FFMpegInfo.cs" />
     <Compile Include="INativeApp.cs" />
     <Compile Include="MbLinkShortcutHandler.cs" />
-    <Compile Include="Migrations\CollectionGroupingMigration.cs" />
     <Compile Include="Migrations\CollectionsViewMigration.cs" />
-    <Compile Include="Migrations\FolderViewSettingMigration.cs" />
     <Compile Include="Migrations\IVersionMigration.cs" />
     <Compile Include="Migrations\DbMigration.cs" />
     <Compile Include="Migrations\MovieDbEpisodeProviderMigration.cs" />
     <Compile Include="Migrations\OmdbEpisodeProviderMigration.cs" />
+    <Compile Include="Migrations\UpdateLevelMigration.cs" />
     <Compile Include="NativeEnvironment.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="StartupOptions.cs" />

+ 0 - 40
MediaBrowser.Server.Startup.Common/Migrations/CollectionGroupingMigration.cs

@@ -1,40 +0,0 @@
-using System.Linq;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-
-namespace MediaBrowser.Server.Startup.Common.Migrations
-{
-    public class CollectionGroupingMigration : IVersionMigration
-    {
-        private readonly IServerConfigurationManager _config;
-        private readonly IUserManager _userManager;
-
-        public CollectionGroupingMigration(IServerConfigurationManager config, IUserManager userManager)
-        {
-            _config = config;
-            _userManager = userManager;
-        }
-
-        public void Run()
-        {
-            var migrationKey = this.GetType().Name;
-            var migrationKeyList = _config.Configuration.Migrations.ToList();
-
-            if (!migrationKeyList.Contains(migrationKey))
-            {
-                if (_config.Configuration.IsStartupWizardCompleted)
-                {
-                    if (_userManager.Users.Any(i => i.Configuration.GroupMoviesIntoBoxSets))
-                    {
-                        _config.Configuration.EnableGroupingIntoCollections = true;
-                    }
-                }
-
-                migrationKeyList.Add(migrationKey);
-                _config.Configuration.Migrations = migrationKeyList.ToArray();
-                _config.SaveConfiguration();
-            }
-
-        }
-    }
-}

+ 0 - 40
MediaBrowser.Server.Startup.Common/Migrations/FolderViewSettingMigration.cs

@@ -1,40 +0,0 @@
-using System.Linq;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-
-namespace MediaBrowser.Server.Startup.Common.Migrations
-{
-    public class FolderViewSettingMigration : IVersionMigration
-    {
-        private readonly IServerConfigurationManager _config;
-        private readonly IUserManager _userManager;
-
-        public FolderViewSettingMigration(IServerConfigurationManager config, IUserManager userManager)
-        {
-            _config = config;
-            _userManager = userManager;
-        }
-
-        public void Run()
-        {
-            var migrationKey = this.GetType().Name;
-            var migrationKeyList = _config.Configuration.Migrations.ToList();
-
-            if (!migrationKeyList.Contains(migrationKey))
-            {
-                if (_config.Configuration.IsStartupWizardCompleted)
-                {
-                    if (_userManager.Users.Any(i => i.Configuration.DisplayFoldersView))
-                    {
-                        _config.Configuration.EnableFolderView = true;
-                    }
-                }
-
-                migrationKeyList.Add(migrationKey);
-                _config.Configuration.Migrations = migrationKeyList.ToArray();
-                _config.SaveConfiguration();
-            }
-
-        }
-    }
-}

+ 97 - 0
MediaBrowser.Server.Startup.Common/Migrations/UpdateLevelMigration.cs

@@ -0,0 +1,97 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Implementations.Updates;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Server.Startup.Common.Migrations
+{
+    public class UpdateLevelMigration : IVersionMigration
+    {
+        private readonly IServerConfigurationManager _config;
+        private readonly IServerApplicationHost _appHost;
+        private readonly IHttpClient _httpClient;
+        private readonly IJsonSerializer _jsonSerializer;
+        private readonly string _releaseAssetFilename;
+
+        public UpdateLevelMigration(IServerConfigurationManager config, IServerApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer, string releaseAssetFilename)
+        {
+            _config = config;
+            _appHost = appHost;
+            _httpClient = httpClient;
+            _jsonSerializer = jsonSerializer;
+            _releaseAssetFilename = releaseAssetFilename;
+        }
+
+        public async void Run()
+        {
+            var lastVersion = _config.Configuration.LastVersion;
+            var currentVersion = _appHost.ApplicationVersion;
+
+            if (string.Equals(lastVersion, currentVersion.ToString(), StringComparison.OrdinalIgnoreCase))
+            {
+                return;
+            }
+
+            try
+            {
+                var updateLevel = _config.Configuration.SystemUpdateLevel;
+
+                // Go down a level
+                if (updateLevel == PackageVersionClass.Release)
+                {
+                    updateLevel = PackageVersionClass.Beta;
+                }
+                else if (updateLevel == PackageVersionClass.Beta)
+                {
+                    updateLevel = PackageVersionClass.Dev;
+                }
+                else if (updateLevel == PackageVersionClass.Dev)
+                {
+                    // It's already dev, there's nothing to check
+                    return;
+                }
+
+                await CheckVersion(currentVersion, updateLevel, CancellationToken.None).ConfigureAwait(false);
+            }
+            catch
+            {
+
+            }
+        }
+
+        private async Task CheckVersion(Version currentVersion, PackageVersionClass updateLevel, CancellationToken cancellationToken)
+        {
+            var result = await new GithubUpdater(_httpClient, _jsonSerializer, TimeSpan.FromMinutes(5))
+                .CheckForUpdateResult("MediaBrowser", "Emby", currentVersion, PackageVersionClass.Beta, _releaseAssetFilename, "MBServer", "Mbserver.zip",
+                    cancellationToken).ConfigureAwait(false);
+
+            if (result != null && result.IsUpdateAvailable)
+            {
+                _config.Configuration.SystemUpdateLevel = updateLevel;
+                _config.SaveConfiguration();
+                return;
+            }
+
+            // Go down a level
+            if (updateLevel == PackageVersionClass.Release)
+            {
+                updateLevel = PackageVersionClass.Beta;
+            }
+            else if (updateLevel == PackageVersionClass.Beta)
+            {
+                updateLevel = PackageVersionClass.Dev;
+            }
+            else
+            {
+                return;
+            }
+
+            await CheckVersion(currentVersion, updateLevel, cancellationToken).ConfigureAwait(false);
+        }
+    }
+}

+ 38 - 26
MediaBrowser.WebDashboard/Api/DashboardService.cs

@@ -157,11 +157,21 @@ namespace MediaBrowser.WebDashboard.Api
             var creator = GetPackageCreator();
             var directory = creator.DashboardUIPath;
 
-            var skipExtensions = GetUndeployedExtensions();
+            var skipExtensions = GetDeployIgnoreExtensions();
+            var skipNames = GetDeployIgnoreFilenames();
 
             return
                 Directory.GetFiles(directory, "*", SearchOption.AllDirectories)
                 .Where(i => !skipExtensions.Contains(Path.GetExtension(i) ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+                .Where(i => !skipNames.Any(s =>
+                {
+                    if (s.Item2)
+                    {
+                        return string.Equals(s.Item1, Path.GetFileName(i), StringComparison.OrdinalIgnoreCase);
+                    }
+
+                    return (Path.GetFileName(i) ?? string.Empty).IndexOf(s.Item1, StringComparison.OrdinalIgnoreCase) != -1;
+                }))
                 .Select(i => i.Replace(directory, string.Empty, StringComparison.OrdinalIgnoreCase).Replace("\\", "/").TrimStart('/') + "?v=" + _appHost.ApplicationVersion.ToString())
                 .ToList();
         }
@@ -300,7 +310,7 @@ namespace MediaBrowser.WebDashboard.Api
             return new PackageCreator(_fileSystem, _localization, Logger, _serverConfigurationManager, _jsonSerializer);
         }
 
-        private List<string> GetUndeployedExtensions()
+        private List<string> GetDeployIgnoreExtensions()
         {
             var list = new List<string>();
 
@@ -315,6 +325,28 @@ namespace MediaBrowser.WebDashboard.Api
             return list;
         }
 
+        private List<Tuple<string,bool>> GetDeployIgnoreFilenames()
+        {
+            var list = new List<Tuple<string, bool>>();
+
+            list.Add(new Tuple<string, bool>("copying", true));
+            list.Add(new Tuple<string, bool>("license", true));
+            list.Add(new Tuple<string, bool>("license-mit", true));
+            list.Add(new Tuple<string, bool>("gitignore", false));
+            list.Add(new Tuple<string, bool>("npmignore", false));
+            list.Add(new Tuple<string, bool>("jshintrc", false));
+            list.Add(new Tuple<string, bool>("gruntfile", false));
+            list.Add(new Tuple<string, bool>("bowerrc", false));
+            list.Add(new Tuple<string, bool>("jscsrc", false));
+            list.Add(new Tuple<string, bool>("hero.svg", false));
+            list.Add(new Tuple<string, bool>("travis.yml", false));
+            list.Add(new Tuple<string, bool>("build.js", false));
+            list.Add(new Tuple<string, bool>("editorconfig", false));
+            list.Add(new Tuple<string, bool>("gitattributes", false));
+
+            return list;
+        }
+
         public async Task<object> Get(GetDashboardPackage request)
         {
             var path = Path.Combine(_serverConfigurationManager.ApplicationPaths.ProgramDataPath,
@@ -344,30 +376,12 @@ namespace MediaBrowser.WebDashboard.Api
             // Try to trim the output size a bit
             var bowerPath = Path.Combine(path, "bower_components");
 
-            if (!string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase))
-            {
-                //var versionedBowerPath = Path.Combine(Path.GetDirectoryName(bowerPath), "bower_components" + _appHost.ApplicationVersion);
-                //Directory.Move(bowerPath, versionedBowerPath);
-                //bowerPath = versionedBowerPath;
-            }
-
-            GetUndeployedExtensions().ForEach(i => DeleteFilesByExtension(bowerPath, i));
+            GetDeployIgnoreExtensions().ForEach(i => DeleteFilesByExtension(bowerPath, i));
 
             DeleteFilesByExtension(bowerPath, ".json", "strings\\");
-            DeleteFilesByName(bowerPath, "copying", true);
-            DeleteFilesByName(bowerPath, "license", true);
-            DeleteFilesByName(bowerPath, "license-mit", true);
-            DeleteFilesByName(bowerPath, "gitignore");
-            DeleteFilesByName(bowerPath, "npmignore");
-            DeleteFilesByName(bowerPath, "jshintrc");
-            DeleteFilesByName(bowerPath, "gruntfile");
-            DeleteFilesByName(bowerPath, "bowerrc");
-            DeleteFilesByName(bowerPath, "jscsrc");
-            DeleteFilesByName(bowerPath, "hero.svg");
-            DeleteFilesByName(bowerPath, "travis.yml");
-            DeleteFilesByName(bowerPath, "build.js");
-            DeleteFilesByName(bowerPath, "editorconfig");
-            DeleteFilesByName(bowerPath, "gitattributes");
+
+            GetDeployIgnoreFilenames().ForEach(i => DeleteFilesByName(bowerPath, i.Item1, i.Item2));
+
             DeleteFoldersByName(bowerPath, "demo");
             DeleteFoldersByName(bowerPath, "test");
             DeleteFoldersByName(bowerPath, "guides");
@@ -382,8 +396,6 @@ namespace MediaBrowser.WebDashboard.Api
             }
 
             _fileSystem.DeleteDirectory(Path.Combine(bowerPath, "jquery", "src"), true);
-            //_fileSystem.DeleteDirectory(Path.Combine(bowerPath, "fingerprintjs2", "flash"), true);
-            //_fileSystem.DeleteDirectory(Path.Combine(bowerPath, "fingerprintjs2", "specs"), true);
 
             DeleteCryptoFiles(Path.Combine(bowerPath, "cryptojslib", "components"));
 

+ 26 - 20
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -101,6 +101,9 @@
     <Content Include="dashboard-ui\autoorganizesmart.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\camerauploadsettings.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\components\appfooter\appfooter.css">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -182,6 +185,12 @@
     <Content Include="dashboard-ui\components\tvproviders\xmltv.template.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\css\images\throbber.gif">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="dashboard-ui\scripts\camerauploadsettings.js">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\scripts\userpasswordpage.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -191,6 +200,9 @@
     <Content Include="dashboard-ui\css\dashboard.css">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\css\autoorganizetable.css">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\css\images\logo.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -320,7 +332,7 @@
     <Content Include="dashboard-ui\scripts\homeupcoming.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\librarydisplay.js">
+    <Content Include="dashboard-ui\dashboard\librarydisplay.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\scripts\livetvguideprovider.js">
@@ -332,7 +344,7 @@
     <Content Include="dashboard-ui\scripts\livetvtunerprovider-m3u.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\livetvtunerprovider-satip.js">
+    <Content Include="dashboard-ui\dashboard\livetvtunerprovider-satip.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\scripts\localsync.js">
@@ -350,7 +362,7 @@
     <Content Include="dashboard-ui\scripts\mysyncsettings.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\autoorganizesmart.js">
+    <Content Include="dashboard-ui\dashboard\autoorganizesmart.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\scripts\searchpage.js">
@@ -371,7 +383,7 @@
     <Content Include="dashboard-ui\scripts\tvlatest.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\wizardcomponents.js">
+    <Content Include="dashboard-ui\dashboard\wizardcomponents.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\scripts\wizardcontroller.js">
@@ -497,7 +509,7 @@
     <Content Include="dashboard-ui\photos.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\dashboardhosting.js">
+    <Content Include="dashboard-ui\dashboard\dashboardhosting.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\scripts\forgotpassword.js">
@@ -857,13 +869,13 @@
     <Content Include="dashboard-ui\scripts\chromecast.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\cinemamodeconfiguration.js">
+    <Content Include="dashboard-ui\dashboard\cinemamodeconfiguration.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\scripts\connectlogin.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\dashboardgeneral.js">
+    <Content Include="dashboard-ui\dashboard\dashboardgeneral.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\scripts\syncactivity.js">
@@ -875,7 +887,7 @@
     <Content Include="dashboard-ui\scripts\devices.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\devicesupload.js">
+    <Content Include="dashboard-ui\dashboard\devicesupload.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\scripts\dlnaprofile.js">
@@ -890,10 +902,10 @@
     <Content Include="dashboard-ui\scripts\encodingsettings.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\autoorganizetv.js">
+    <Content Include="dashboard-ui\dashboard\autoorganizetv.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\autoorganizelog.js">
+    <Content Include="dashboard-ui\dashboard\autoorganizelog.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\scripts\externalplayer.js">
@@ -989,12 +1001,6 @@
     <Content Include="dashboard-ui\scripts\livetvsuggested.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\sync.js">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="dashboard-ui\scripts\thememediaplayer.js">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
     <Content Include="dashboard-ui\scripts\tvupcoming.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -1086,7 +1092,7 @@
     <Content Include="dashboard-ui\scripts\edititemmetadata.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\librarysettings.js">
+    <Content Include="dashboard-ui\dashboard\librarysettings.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\scripts\musicrecommended.js">
@@ -1353,7 +1359,7 @@
     </Content>
   </ItemGroup>
   <ItemGroup>
-    <Content Include="dashboard-ui\scripts\logpage.js">
+    <Content Include="dashboard-ui\dashboard\logpage.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
   </ItemGroup>
@@ -1426,7 +1432,7 @@
     </Content>
   </ItemGroup>
   <ItemGroup>
-    <Content Include="dashboard-ui\scripts\aboutpage.js">
+    <Content Include="dashboard-ui\dashboard\aboutpage.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\css\images\supporter\supporterflag.png">
@@ -1438,7 +1444,7 @@
     <Content Include="dashboard-ui\itemlist.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\wizardfinishpage.js">
+    <Content Include="dashboard-ui\dashboard\wizardfinishpage.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\css\images\items\detail\video.png">

+ 9 - 0
MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs

@@ -827,6 +827,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                         }
                         break;
                     }
+                case "tvmazeid":
+                    {
+                        var id = reader.ReadElementContentAsString();
+                        if (!string.IsNullOrWhiteSpace(id))
+                        {
+                            item.SetProviderId(MetadataProviders.TvMaze, id);
+                        }
+                        break;
+                    }
                 case "audiodbartistid":
                     {
                         var id = reader.ReadElementContentAsString();

+ 6 - 0
MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs

@@ -822,6 +822,12 @@ namespace MediaBrowser.XbmcMetadata.Savers
                 writer.WriteElementString("tvrageid", externalId);
             }
 
+            externalId = item.GetProviderId(MetadataProviders.TvMaze);
+            if (!string.IsNullOrEmpty(externalId))
+            {
+                writer.WriteElementString("tvmazeid", externalId);
+            }
+
             if (options.SaveImagePathsInNfo)
             {
                 AddImages(item, writer, libraryManager, config);

+ 3 - 1
MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs

@@ -118,7 +118,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
                     "airsbefore_season",
                     "DVD_episodenumber",
                     "DVD_season",
-                    "absolute_number"
+                    "absolute_number",
+                    "displayseason",
+                    "displayepisode"
             };
 
             return list;

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.654</version>
+        <version>3.0.655</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Emby 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.654" />
+            <dependency id="MediaBrowser.Common" version="3.0.655" />
             <dependency id="NLog" version="4.3.6" />
             <dependency id="SimpleInjector" version="3.2.0" />
         </dependencies>

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.654</version>
+        <version>3.0.655</version>
         <title>MediaBrowser.Common</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.654</version>
+        <version>3.0.655</version>
         <title>Media Browser.Server.Core</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Emby Server.</description>
         <copyright>Copyright © Emby 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.654" />
+            <dependency id="MediaBrowser.Common" version="3.0.655" />
 			<dependency id="Interfaces.IO" version="1.0.0.5" />
         </dependencies>
     </metadata>