Browse Source

Merge branch 'dev'

Luke Pulverenti 9 năm trước cách đây
mục cha
commit
f6ef727321
100 tập tin đã thay đổi với 840 bổ sung631 xóa
  1. 1 0
      MediaBrowser.Api/BrandingService.cs
  2. 6 0
      MediaBrowser.Api/ItemUpdateService.cs
  3. 3 8
      MediaBrowser.Api/Movies/MoviesService.cs
  4. 25 9
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  5. 0 12
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  6. 35 38
      MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
  7. 0 1
      MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
  8. 5 2
      MediaBrowser.Api/Playback/MediaInfoService.cs
  9. 0 1
      MediaBrowser.Api/Playback/Progressive/VideoService.cs
  10. 13 1
      MediaBrowser.Api/Playback/StreamState.cs
  11. 4 4
      MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
  12. 2 6
      MediaBrowser.Api/UserLibrary/UserLibraryService.cs
  13. 4 10
      MediaBrowser.Api/VideosService.cs
  14. 6 4
      MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
  15. 3 3
      MediaBrowser.Common.Implementations/packages.config
  16. 16 18
      MediaBrowser.Controller/Entities/Audio/Audio.cs
  17. 15 17
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  18. 10 9
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  19. 5 6
      MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
  20. 32 15
      MediaBrowser.Controller/Entities/BaseItem.cs
  21. 24 3
      MediaBrowser.Controller/Entities/CollectionFolder.cs
  22. 1 30
      MediaBrowser.Controller/Entities/Folder.cs
  23. 4 3
      MediaBrowser.Controller/Entities/Game.cs
  24. 5 6
      MediaBrowser.Controller/Entities/GameGenre.cs
  25. 6 7
      MediaBrowser.Controller/Entities/GameSystem.cs
  26. 5 6
      MediaBrowser.Controller/Entities/Genre.cs
  27. 6 1
      MediaBrowser.Controller/Entities/IHasUserData.cs
  28. 1 0
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  29. 0 30
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  30. 0 9
      MediaBrowser.Controller/Entities/MusicVideo.cs
  31. 5 6
      MediaBrowser.Controller/Entities/Person.cs
  32. 5 6
      MediaBrowser.Controller/Entities/Studio.cs
  33. 25 24
      MediaBrowser.Controller/Entities/TV/Episode.cs
  34. 29 13
      MediaBrowser.Controller/Entities/TV/Season.cs
  35. 84 29
      MediaBrowser.Controller/Entities/TV/Series.cs
  36. 1 21
      MediaBrowser.Controller/Entities/Trailer.cs
  37. 5 5
      MediaBrowser.Controller/Entities/UserViewBuilder.cs
  38. 103 28
      MediaBrowser.Controller/Entities/Video.cs
  39. 5 6
      MediaBrowser.Controller/Entities/Year.cs
  40. 17 15
      MediaBrowser.Controller/Library/ILibraryManager.cs
  41. 5 0
      MediaBrowser.Controller/Library/IUserDataManager.cs
  42. 1 1
      MediaBrowser.Controller/Library/TVUtils.cs
  43. 2 5
      MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs
  44. 2 0
      MediaBrowser.Controller/LiveTv/ITunerHost.cs
  45. 0 11
      MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
  46. 14 6
      MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
  47. 16 12
      MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
  48. 0 26
      MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
  49. 2 1
      MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
  50. 0 6
      MediaBrowser.Controller/Notifications/INotificationsRepository.cs
  51. 0 6
      MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs
  52. 0 6
      MediaBrowser.Controller/Persistence/IItemRepository.cs
  53. 0 6
      MediaBrowser.Controller/Persistence/IUserDataRepository.cs
  54. 0 6
      MediaBrowser.Controller/Providers/IProviderRepository.cs
  55. 6 2
      MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs
  56. 6 3
      MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
  57. 6 3
      MediaBrowser.Dlna/Didl/DidlBuilder.cs
  58. 6 2
      MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
  59. 7 4
      MediaBrowser.Dlna/PlayTo/PlayToController.cs
  60. 6 2
      MediaBrowser.Dlna/PlayTo/PlayToManager.cs
  61. 18 17
      MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs
  62. 3 3
      MediaBrowser.LocalMetadata/BaseXmlProvider.cs
  63. 21 4
      MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
  64. 9 3
      MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
  65. 1 0
      MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
  66. 16 0
      MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
  67. 24 2
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  68. 0 1
      MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
  69. 11 0
      MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
  70. 1 0
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  71. 13 0
      MediaBrowser.Model/Dlna/ILocalPlayer.cs
  72. 32 4
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  73. 1 1
      MediaBrowser.Model/Dlna/TranscodingProfile.cs
  74. 2 0
      MediaBrowser.Model/Dto/BaseItemDto.cs
  75. 2 0
      MediaBrowser.Model/Entities/MediaStream.cs
  76. 1 0
      MediaBrowser.Model/LiveTv/LiveTvOptions.cs
  77. 2 0
      MediaBrowser.Model/Querying/ItemFields.cs
  78. 2 0
      MediaBrowser.Model/Sync/SyncJobItem.cs
  79. 1 1
      MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
  80. 2 2
      MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
  81. 9 14
      MediaBrowser.Providers/Manager/MetadataService.cs
  82. 9 0
      MediaBrowser.Providers/Manager/ProviderUtils.cs
  83. 5 1
      MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
  84. 1 1
      MediaBrowser.Providers/Movies/FanArtMovieUpdatesPostScanTask.cs
  85. 7 5
      MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
  86. 2 2
      MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
  87. 4 4
      MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
  88. 6 6
      MediaBrowser.Providers/Movies/MovieDbProvider.cs
  89. 2 2
      MediaBrowser.Providers/Movies/MovieDbSearch.cs
  90. 2 2
      MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs
  91. 5 5
      MediaBrowser.Providers/Movies/MovieExternalIds.cs
  92. 1 1
      MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs
  93. 1 1
      MediaBrowser.Providers/Movies/TmdbSettings.cs
  94. 3 3
      MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
  95. 4 4
      MediaBrowser.Providers/Music/FanArtArtistProvider.cs
  96. 1 1
      MediaBrowser.Providers/Music/FanArtUpdatesPostScanTask.cs
  97. 2 2
      MediaBrowser.Providers/Music/MovieDbMusicVideoProvider.cs
  98. 9 9
      MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
  99. 4 4
      MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
  100. 6 6
      MediaBrowser.Providers/Music/MusicExternalIds.cs

+ 1 - 0
MediaBrowser.Api/BrandingService.cs

@@ -10,6 +10,7 @@ namespace MediaBrowser.Api
     }
 
     [Route("/Branding/Css", "GET", Summary = "Gets custom css")]
+    [Route("/Branding/Css.css", "GET", Summary = "Gets custom css")]
     public class GetBrandingCss
     {
     }

+ 6 - 0
MediaBrowser.Api/ItemUpdateService.cs

@@ -247,6 +247,12 @@ namespace MediaBrowser.Api
                 hasBudget.Revenue = request.Revenue;
             }
 
+            var hasOriginalTitle = item as IHasOriginalTitle;
+            if (hasOriginalTitle != null)
+            {
+                hasOriginalTitle.OriginalTitle = hasOriginalTitle.OriginalTitle;
+            }
+
             var hasCriticRating = item as IHasCriticRating;
             if (hasCriticRating != null)
             {

+ 3 - 8
MediaBrowser.Api/Movies/MoviesService.cs

@@ -197,12 +197,7 @@ namespace MediaBrowser.Api.Movies
 
             var parentIds = new string[] { };
             var list = _libraryManager.GetItemList(query, parentIds)
-                .Where(i =>
-                {
-                    // Strip out secondary versions
-                    var v = i as Video;
-                    return v != null && !v.PrimaryVersionId.HasValue;
-                })
+                .DistinctBy(i => i.PresentationUniqueKey, StringComparer.OrdinalIgnoreCase)
                 .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
                 .ToList();
 
@@ -247,7 +242,7 @@ namespace MediaBrowser.Api.Movies
             var recentlyPlayedMovies = allMoviesForCategories
                 .Select(i =>
                 {
-                    var userdata = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey());
+                    var userdata = _userDataRepository.GetUserData(user, i);
                     return new Tuple<BaseItem, bool, DateTime>(i, userdata.Played, userdata.LastPlayedDate ?? DateTime.MinValue);
                 })
                 .Where(i => i.Item2)
@@ -260,7 +255,7 @@ namespace MediaBrowser.Api.Movies
                 .Select(i =>
                 {
                     var score = 0;
-                    var userData = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey());
+                    var userData = _userDataRepository.GetUserData(user, i);
 
                     if (userData.IsFavorite)
                     {

+ 25 - 9
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -288,9 +288,9 @@ namespace MediaBrowser.Api.Playback
         {
             if (string.Equals(ApiEntryPoint.Instance.GetEncodingOptions().HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
             {
-                
-                    return "h264_qsv";
-               
+
+                return "h264_qsv";
+
             }
 
             return "libx264";
@@ -821,9 +821,14 @@ namespace MediaBrowser.Api.Playback
         /// <returns>System.String.</returns>
         protected string GetVideoDecoder(StreamState state)
         {
-            if (string.Equals(ApiEntryPoint.Instance.GetEncodingOptions().HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
+            if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+            {
+                return null;
+            }
+
+            if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec))
             {
-                if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec))
+                if (string.Equals(ApiEntryPoint.Instance.GetEncodingOptions().HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
                 {
                     switch (state.MediaSource.VideoStream.Codec.ToLower())
                     {
@@ -831,7 +836,8 @@ namespace MediaBrowser.Api.Playback
                         case "h264":
                             if (MediaEncoder.SupportsDecoder("h264_qsv"))
                             {
-                                return "-c:v h264_qsv ";
+                                // Seeing stalls and failures with decoding. Not worth it compared to encoding.
+                                //return "-c:v h264_qsv ";
                             }
                             break;
                         case "mpeg2video":
@@ -1033,7 +1039,7 @@ namespace MediaBrowser.Api.Playback
             process.BeginOutputReadLine();
 
             // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
-            StartStreamingLog(transcodingJob, state, process.StandardError.BaseStream, state.LogFileStream);
+            Task.Run(() => StartStreamingLog(transcodingJob, state, process.StandardError.BaseStream, state.LogFileStream));
 
             // Wait for the file to exist before proceeeding
             while (!FileSystem.FileExists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
@@ -1076,7 +1082,7 @@ namespace MediaBrowser.Api.Playback
             return true;
         }
 
-        private async void StartStreamingLog(TranscodingJob transcodingJob, StreamState state, Stream source, Stream target)
+        private async Task StartStreamingLog(TranscodingJob transcodingJob, StreamState state, Stream source, Stream target)
         {
             try
             {
@@ -1804,6 +1810,15 @@ namespace MediaBrowser.Api.Playback
                 }
             }
 
+            if (string.Equals("h264", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
+            {
+                if (videoStream.IsAVC.HasValue && !videoStream.IsAVC.Value)
+                {
+                    Logger.Debug("Cannot stream copy video. Stream is marked as not AVC");
+                    return false;
+                }
+            }
+
             // Source and target codecs must match
             if (!string.Equals(request.VideoCodec, videoStream.Codec, StringComparison.OrdinalIgnoreCase))
             {
@@ -2222,9 +2237,10 @@ namespace MediaBrowser.Api.Playback
 
             if (state.VideoRequest != null)
             {
+                // Important: If this is ever re-enabled, make sure not to use it with wtv because it breaks seeking
                 if (string.Equals(state.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase) && state.VideoRequest.CopyTimestamps)
                 {
-                    inputModifier += " -noaccurate_seek";
+                    //inputModifier += " -noaccurate_seek";
                 }
             }
 

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

@@ -289,17 +289,5 @@ namespace MediaBrowser.Api.Playback.Hls
 
             return isLiveStream;
         }
-
-        protected override bool CanStreamCopyAudio(StreamState state, List<string> supportedAudioCodecs)
-        {
-            var isLiveStream = IsLiveStream(state);
-
-            if (!isLiveStream)
-            {
-                return false;
-            }
-
-            return base.CanStreamCopyAudio(state, supportedAudioCodecs);
-        }
     }
 }

+ 35 - 38
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -500,18 +500,6 @@ namespace MediaBrowser.Api.Playback.Hls
             return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
         }
 
-        private bool IsLiveStream(StreamState state)
-        {
-            var isLiveStream = (state.RunTimeTicks ?? 0) == 0;
-
-            if (state.VideoRequest.ForceLiveStream)
-            {
-                return true;
-            }
-
-            return isLiveStream;
-        }
-
         private string GetMasterPlaylistFileText(StreamState state, int totalBitrate)
         {
             var builder = new StringBuilder();
@@ -830,11 +818,10 @@ namespace MediaBrowser.Api.Playback.Hls
             {
                 if (state.VideoStream != null && IsH264(state.VideoStream) && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
                 {
-                    Logger.Debug("Enabling h264_mp4toannexb due to nal_length_size of {0}", state.VideoStream.NalLengthSize);
                     args += " -bsf:v h264_mp4toannexb";
                 }
 
-                args += " -flags -global_header -sc_threshold 0";
+                args += " -flags -global_header";
             }
             else
             {
@@ -859,7 +846,12 @@ namespace MediaBrowser.Api.Playback.Hls
                     args += GetGraphicalSubtitleParam(state, codec);
                 }
 
-                args += " -flags -global_header -sc_threshold 0";
+                args += " -flags -global_header";
+            }
+
+            if (EnableCopyTs(state) && args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1)
+            {
+                args += " -copyts";
             }
 
             return args;
@@ -867,7 +859,8 @@ namespace MediaBrowser.Api.Playback.Hls
 
         private bool EnableCopyTs(StreamState state)
         {
-            return state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
+            //return state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
+            return true;
         }
 
         protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
@@ -889,24 +882,28 @@ namespace MediaBrowser.Api.Playback.Hls
 
             var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty;
 
-            //var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state);
-
-            //return string.Format("{0} {11} {1}{10} -map_metadata -1 -threads {2} {3} {4} {5} -f segment -segment_time {6} -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
-            //    inputModifier,
-            //    GetInputArgument(state),
-            //    threads,
-            //    mapArgs,
-            //    GetVideoArguments(state),
-            //    GetAudioArguments(state),
-            //    state.SegmentLength.ToString(UsCulture),
-            //    startNumberParam,
-            //    outputPath,
-            //    outputTsArg,
-            //            slowSeekParam,
-            //            toTimeParam
-            //    ).Trim();
-
-            return string.Format("{0}{11} {1} -map_metadata -1 -threads {2} {3} {4}{5} {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"",
+            var enableGenericSegmenter = false;
+
+            if (enableGenericSegmenter)
+            {
+                var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state);
+
+                return string.Format("{0} {10} {1} -map_metadata -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
+                    inputModifier,
+                    GetInputArgument(state),
+                    threads,
+                    mapArgs,
+                    GetVideoArguments(state),
+                    GetAudioArguments(state),
+                    state.SegmentLength.ToString(UsCulture),
+                    startNumberParam,
+                    outputPath,
+                    outputTsArg,
+                            toTimeParam
+                    ).Trim();
+            }
+
+            return string.Format("{0}{11} {1} -map_metadata -1 -threads {2} {3} {4}{5} {6} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"",
                             inputModifier,
                             GetInputArgument(state),
                             threads,
@@ -946,10 +943,10 @@ namespace MediaBrowser.Api.Playback.Hls
         {
             var isLiveStream = IsLiveStream(state);
 
-            if (!isLiveStream)
-            {
-                return false;
-            }
+            //if (!isLiveStream && Request.QueryString["AllowCustomSegmenting"] != "true")
+            //{
+            //    return false;
+            //}
 
             return base.CanStreamCopyVideo(state);
         }

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

@@ -89,7 +89,6 @@ namespace MediaBrowser.Api.Playback.Hls
                 // if h264_mp4toannexb is ever added, do not use it for live tv
                 if (state.VideoStream != null && IsH264(state.VideoStream) && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
                 {
-                    Logger.Debug("Enabling h264_mp4toannexb due to nal_length_size of {0}", state.VideoStream.NalLengthSize);
                     args += " -bsf:v h264_mp4toannexb";
                 }
                 return args;

+ 5 - 2
MediaBrowser.Api/Playback/MediaInfoService.cs

@@ -15,6 +15,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.MediaEncoding;
 
 namespace MediaBrowser.Api.Playback
 {
@@ -66,14 +67,16 @@ namespace MediaBrowser.Api.Playback
         private readonly ILibraryManager _libraryManager;
         private readonly IServerConfigurationManager _config;
         private readonly INetworkManager _networkManager;
+        private readonly IMediaEncoder _mediaEncoder;
 
-        public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager)
+        public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager, IMediaEncoder mediaEncoder)
         {
             _mediaSourceManager = mediaSourceManager;
             _deviceManager = deviceManager;
             _libraryManager = libraryManager;
             _config = config;
             _networkManager = networkManager;
+            _mediaEncoder = mediaEncoder;
         }
 
         public object Get(GetBitrateTestBytes request)
@@ -241,7 +244,7 @@ namespace MediaBrowser.Api.Playback
             int? subtitleStreamIndex,
             string playSessionId)
         {
-            var streamBuilder = new StreamBuilder(Logger);
+            var streamBuilder = new StreamBuilder(_mediaEncoder, Logger);
 
             var options = new VideoOptions
             {

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

@@ -141,7 +141,6 @@ namespace MediaBrowser.Api.Playback.Progressive
             {
                 if (state.VideoStream != null && IsH264(state.VideoStream) && string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
                 {
-                    Logger.Debug("Enabling h264_mp4toannexb due to nal_length_size of {0}", state.VideoStream.NalLengthSize);
                     args += " -bsf:v h264_mp4toannexb";
                 }
 

+ 13 - 1
MediaBrowser.Api/Playback/StreamState.cs

@@ -69,7 +69,19 @@ namespace MediaBrowser.Api.Playback
 
         public List<string> PlayableStreamFileNames { get; set; }
 
-        public int SegmentLength = 3;
+        public int SegmentLength
+        {
+            get
+            {
+                if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+                {
+                    return 10;
+                }
+
+                return 3;
+            }
+        }
+
         public int HlsListSize
         {
             get

+ 4 - 4
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -274,7 +274,7 @@ namespace MediaBrowser.Api.UserLibrary
             {
                 items = items.Where(i =>
                     {
-                        var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey());
+                        var userdata = UserDataRepository.GetUserData(user, i);
 
                         return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
                     });
@@ -284,7 +284,7 @@ namespace MediaBrowser.Api.UserLibrary
             {
                 items = items.Where(i =>
                 {
-                    var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey());
+                    var userdata = UserDataRepository.GetUserData(user, i);
 
                     return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
                 });
@@ -294,7 +294,7 @@ namespace MediaBrowser.Api.UserLibrary
             {
                 items = items.Where(i =>
                 {
-                    var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey());
+                    var userdata = UserDataRepository.GetUserData(user, i);
 
                     var likes = userdata.Likes ?? false;
                     var favorite = userdata.IsFavorite;
@@ -307,7 +307,7 @@ namespace MediaBrowser.Api.UserLibrary
             {
                 items = items.Where(i =>
                 {
-                    var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey());
+                    var userdata = UserDataRepository.GetUserData(user, i);
 
                     return userdata != null && userdata.IsFavorite;
                 });

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

@@ -519,10 +519,8 @@ namespace MediaBrowser.Api.UserLibrary
 
             var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId);
 
-            var key = item.GetUserDataKey();
-
             // Get the user data for this item
-            var data = _userDataRepository.GetUserData(user.Id, key);
+            var data = _userDataRepository.GetUserData(user, item);
 
             // Set favorite status
             data.IsFavorite = isFavorite;
@@ -567,10 +565,8 @@ namespace MediaBrowser.Api.UserLibrary
 
             var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId);
 
-            var key = item.GetUserDataKey();
-
             // Get the user data for this item
-            var data = _userDataRepository.GetUserData(user.Id, key);
+            var data = _userDataRepository.GetUserData(user, item);
 
             data.Likes = likes;
 

+ 4 - 10
MediaBrowser.Api/VideosService.cs

@@ -130,6 +130,7 @@ namespace MediaBrowser.Api
             var items = request.Ids.Split(',')
                 .Select(i => new Guid(i))
                 .Select(i => _libraryManager.GetItemById(i))
+                .OfType<Video>()
                 .ToList();
 
             if (items.Count < 2)
@@ -137,14 +138,7 @@ namespace MediaBrowser.Api
                 throw new ArgumentException("Please supply at least two videos to merge.");
             }
 
-            if (items.Any(i => !(i is Video)))
-            {
-                throw new ArgumentException("Only videos can be grouped together.");
-            }
-
-            var videos = items.Cast<Video>().ToList();
-
-            var videosWithVersions = videos.Where(i => i.MediaSourceCount > 1)
+            var videosWithVersions = items.Where(i => i.MediaSourceCount > 1)
                 .ToList();
 
             if (videosWithVersions.Count > 1)
@@ -156,7 +150,7 @@ namespace MediaBrowser.Api
 
             if (primaryVersion == null)
             {
-                primaryVersion = videos.OrderBy(i =>
+                primaryVersion = items.OrderBy(i =>
                 {
                     if (i.Video3DFormat.HasValue)
                     {
@@ -179,7 +173,7 @@ namespace MediaBrowser.Api
                     }).First();
             }
 
-            foreach (var item in videos.Where(i => i.Id != primaryVersion.Id))
+            foreach (var item in items.Where(i => i.Id != primaryVersion.Id))
             {
                 item.PrimaryVersionId = primaryVersion.Id;
 

+ 6 - 4
MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj

@@ -54,8 +54,9 @@
     <Reference Include="MoreLinq">
       <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
     </Reference>
-    <Reference Include="NLog">
-      <HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
+    <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
+      <HintPath>..\packages\NLog.4.3.1\lib\net45\NLog.dll</HintPath>
+      <Private>True</Private>
     </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
@@ -64,8 +65,9 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
     </Reference>
-    <Reference Include="SimpleInjector">
-      <HintPath>..\packages\SimpleInjector.3.1.2\lib\net45\SimpleInjector.dll</HintPath>
+    <Reference Include="SimpleInjector, Version=3.1.3.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
+      <HintPath>..\packages\SimpleInjector.3.1.3\lib\net45\SimpleInjector.dll</HintPath>
+      <Private>True</Private>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Configuration" />

+ 3 - 3
MediaBrowser.Common.Implementations/packages.config

@@ -2,7 +2,7 @@
 <packages>
   <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
   <package id="morelinq" version="1.4.0" targetFramework="net45" />
-  <package id="NLog" version="4.2.3" targetFramework="net45" />
+  <package id="NLog" version="4.3.1" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
-  <package id="SimpleInjector" version="3.1.2" targetFramework="net45" />
-</packages>
+  <package id="SimpleInjector" version="3.1.3" targetFramework="net45" />
+</packages>

+ 16 - 18
MediaBrowser.Controller/Entities/Audio/Audio.cs

@@ -26,7 +26,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         IArchivable
     {
         public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
-        
+
         public long? Size { get; set; }
         public string Container { get; set; }
         public int? TotalBitrate { get; set; }
@@ -150,12 +150,10 @@ namespace MediaBrowser.Controller.Entities.Audio
                     + (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name;
         }
 
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
+            var list = base.GetUserDataKeys();
+
             if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys)
             {
                 var songKey = IndexNumber.HasValue ? IndexNumber.Value.ToString("0000") : string.Empty;
@@ -165,7 +163,7 @@ namespace MediaBrowser.Controller.Entities.Audio
                 {
                     songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey;
                 }
-                songKey+= Name;
+                songKey += Name;
 
                 if (!string.IsNullOrWhiteSpace(Album))
                 {
@@ -178,25 +176,25 @@ namespace MediaBrowser.Controller.Entities.Audio
                     songKey = albumArtist + "-" + songKey;
                 }
 
-                return songKey;
+                list.Insert(0, songKey);
             }
-
-            var parent = AlbumEntity;
-
-            if (parent != null)
+            else
             {
-                var parentKey = parent.GetUserDataKey();
+                var parent = AlbumEntity;
 
-                if (IndexNumber.HasValue)
+                if (parent != null && IndexNumber.HasValue)
                 {
-                    var songKey = (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("0000 - ") : "")
-                                  + IndexNumber.Value.ToString("0000 - ");
+                    list.InsertRange(0, parent.GetUserDataKeys().Select(i =>
+                    {
+                        var songKey = (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("0000 - ") : "")
+                                      + IndexNumber.Value.ToString("0000 - ");
 
-                    return parentKey + songKey;
+                        return i + songKey;
+                    }));
                 }
             }
 
-            return base.CreateUserDataKey();
+            return list;
         }
 
         public override UnratedItem GetBlockUnratedType()

+ 15 - 17
MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

@@ -96,36 +96,34 @@ namespace MediaBrowser.Controller.Entities.Audio
 
         public List<string> Artists { get; set; }
 
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            var id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
+            var list = base.GetUserDataKeys();
 
-            if (!string.IsNullOrWhiteSpace(id))
+            if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys)
             {
-                return "MusicAlbum-MusicBrainzReleaseGroup-" + id;
+                var albumArtist = AlbumArtist;
+                if (!string.IsNullOrWhiteSpace(albumArtist))
+                {
+                    list.Insert(0, albumArtist + "-" + Name);
+                }
             }
 
-            id = this.GetProviderId(MetadataProviders.MusicBrainzAlbum);
+            var id = this.GetProviderId(MetadataProviders.MusicBrainzAlbum);
 
             if (!string.IsNullOrWhiteSpace(id))
             {
-                return "MusicAlbum-Musicbrainz-" + id;
+                list.Insert(0, "MusicAlbum-Musicbrainz-" + id);
             }
 
-            if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys)
+            id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
+
+            if (!string.IsNullOrWhiteSpace(id))
             {
-                var albumArtist = AlbumArtist;
-                if (!string.IsNullOrWhiteSpace(albumArtist))
-                {
-                    return albumArtist + "-" + Name;
-                }
+                list.Insert(0, "MusicAlbum-MusicBrainzReleaseGroup-" + id);
             }
 
-            return base.CreateUserDataKey();
+            return list;
         }
 
         protected override bool GetBlockUnratedValue(UserPolicy config)

+ 10 - 9
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -80,13 +80,12 @@ namespace MediaBrowser.Controller.Entities.Audio
             ProductionLocations = new List<string>();
         }
 
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            return GetUserDataKey(this);
+            var list = base.GetUserDataKeys();
+
+            list.InsertRange(0, GetUserDataKeys(this));
+            return list;
         }
 
         /// <summary>
@@ -121,16 +120,18 @@ namespace MediaBrowser.Controller.Entities.Audio
         /// </summary>
         /// <param name="item">The item.</param>
         /// <returns>System.String.</returns>
-        private static string GetUserDataKey(MusicArtist item)
+        private static List<string> GetUserDataKeys(MusicArtist item)
         {
+            var list = new List<string>();
             var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
 
             if (!string.IsNullOrEmpty(id))
             {
-                return "Artist-Musicbrainz-" + id;
+                list.Add("Artist-Musicbrainz-" + id);
             }
 
-            return "Artist-" + item.Name;
+            list.Add("Artist-" + item.Name);
+            return list;
         }
 
         protected override bool GetBlockUnratedValue(UserPolicy config)

+ 5 - 6
MediaBrowser.Controller/Entities/Audio/MusicGenre.cs

@@ -10,13 +10,12 @@ namespace MediaBrowser.Controller.Entities.Audio
     /// </summary>
     public class MusicGenre : BaseItem, IItemByName
     {
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            return "MusicGenre-" + Name;
+            var list = base.GetUserDataKeys();
+
+            list.Insert(0, "MusicGenre-" + Name);
+            return list;
         }
 
         [IgnoreDataMember]

+ 32 - 15
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -125,6 +125,8 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        public string OriginalTitle { get; set; }
+
         /// <summary>
         /// Gets or sets the id.
         /// </summary>
@@ -1147,6 +1149,12 @@ namespace MediaBrowser.Controller.Entities
             get { return null; }
         }
 
+        [IgnoreDataMember]
+        public virtual string PresentationUniqueKey
+        {
+            get { return Id.ToString("N"); }
+        }
+
         private string _userDataKey;
         /// <summary>
         /// Gets the user data key.
@@ -1156,7 +1164,7 @@ namespace MediaBrowser.Controller.Entities
         {
             if (string.IsNullOrWhiteSpace(_userDataKey))
             {
-                var key = CreateUserDataKey();
+                var key = GetUserDataKeys().First();
                 _userDataKey = key;
                 return key;
             }
@@ -1164,16 +1172,20 @@ namespace MediaBrowser.Controller.Entities
             return _userDataKey;
         }
 
-        protected virtual string CreateUserDataKey()
+        public virtual List<string> GetUserDataKeys()
         {
+            var list = new List<string>();
+
             if (SourceType == SourceType.Channel)
             {
                 if (!string.IsNullOrWhiteSpace(ExternalId))
                 {
-                    return ExternalId;
+                    list.Add(ExternalId);
                 }
             }
-            return Id.ToString();
+
+            list.Add(Id.ToString());
+            return list;
         }
 
         internal virtual bool IsValidFromResolver(BaseItem newItem)
@@ -1540,11 +1552,11 @@ namespace MediaBrowser.Controller.Entities
         {
             if (!string.IsNullOrEmpty(info.Path))
             {
-                var itemByPath = LibraryManager.FindByPath(info.Path);
+                var itemByPath = LibraryManager.FindByPath(info.Path, null);
 
                 if (itemByPath == null)
                 {
-                    Logger.Warn("Unable to find linked item at path {0}", info.Path);
+                    //Logger.Warn("Unable to find linked item at path {0}", info.Path);
                 }
 
                 return itemByPath;
@@ -1553,6 +1565,15 @@ namespace MediaBrowser.Controller.Entities
             return null;
         }
 
+        [IgnoreDataMember]
+        public virtual bool EnableRememberingTrackSelections
+        {
+            get
+            {
+                return true;
+            }
+        }
+
         /// <summary>
         /// Adds a studio to the item
         /// </summary>
@@ -1606,9 +1627,7 @@ namespace MediaBrowser.Controller.Entities
                 throw new ArgumentNullException();
             }
 
-            var key = GetUserDataKey();
-
-            var data = UserDataManager.GetUserData(user.Id, key);
+            var data = UserDataManager.GetUserData(user, this);
 
             if (datePlayed.HasValue)
             {
@@ -1643,9 +1662,7 @@ namespace MediaBrowser.Controller.Entities
                 throw new ArgumentNullException();
             }
 
-            var key = GetUserDataKey();
-
-            var data = UserDataManager.GetUserData(user.Id, key);
+            var data = UserDataManager.GetUserData(user, this);
 
             //I think it is okay to do this here.
             // if this is only called when a user is manually forcing something to un-played
@@ -1976,14 +1993,14 @@ namespace MediaBrowser.Controller.Entities
 
         public virtual bool IsPlayed(User user)
         {
-            var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey());
+            var userdata = UserDataManager.GetUserData(user, this);
 
             return userdata != null && userdata.Played;
         }
 
         public bool IsFavoriteOrLiked(User user)
         {
-            var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey());
+            var userdata = UserDataManager.GetUserData(user, this);
 
             return userdata != null && (userdata.IsFavorite || (userdata.Likes ?? false));
         }
@@ -1995,7 +2012,7 @@ namespace MediaBrowser.Controller.Entities
                 throw new ArgumentNullException("user");
             }
 
-            var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey());
+            var userdata = UserDataManager.GetUserData(user, this);
 
             return userdata == null || !userdata.Played;
         }

+ 24 - 3
MediaBrowser.Controller/Entities/CollectionFolder.cs

@@ -8,6 +8,7 @@ using System.Runtime.Serialization;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using MoreLinq;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -97,7 +98,6 @@ namespace MediaBrowser.Controller.Entities
                 }
             }
 
-
             return base.IsValidFromResolver(newItem);
         }
 
@@ -200,9 +200,30 @@ namespace MediaBrowser.Controller.Entities
 
         public IEnumerable<Folder> GetPhysicalParents()
         {
-            return LibraryManager.RootFolder.Children
+            var rootChildren = LibraryManager.RootFolder.Children
                 .OfType<Folder>()
-                .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase));
+                .ToList();
+
+            return PhysicalLocations.Where(i => !string.Equals(i, Path, StringComparison.OrdinalIgnoreCase)).SelectMany(i => GetPhysicalParents(i, rootChildren)).DistinctBy(i => i.Id);
+        }
+
+        private IEnumerable<Folder> GetPhysicalParents(string path, List<Folder> rootChildren)
+        {
+            var result = rootChildren
+                .Where(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase))
+                .ToList();
+
+            if (result.Count == 0)
+            {
+                var folder = LibraryManager.FindByPath(path, true) as Folder;
+
+                if (folder != null)
+                {
+                    result.Add(folder);
+                }
+            }
+
+            return result;
         }
 
         [IgnoreDataMember]

+ 1 - 30
MediaBrowser.Controller/Entities/Folder.cs

@@ -839,11 +839,6 @@ namespace MediaBrowser.Controller.Entities
                     Logger.Debug("Query requires post-filtering due to ItemSortBy.SeriesSortName");
                     return true;
                 }
-                if (query.SortBy.Contains(ItemSortBy.StartDate, StringComparer.OrdinalIgnoreCase))
-                {
-                    Logger.Debug("Query requires post-filtering due to ItemSortBy.StartDate");
-                    return true;
-                }
                 if (query.SortBy.Contains(ItemSortBy.Studio, StringComparer.OrdinalIgnoreCase))
                 {
                     Logger.Debug("Query requires post-filtering due to ItemSortBy.Studio");
@@ -1059,30 +1054,6 @@ namespace MediaBrowser.Controller.Entities
                 return true;
             }
 
-            if (!string.IsNullOrWhiteSpace(query.NameContains))
-            {
-                Logger.Debug("Query requires post-filtering due to NameContains");
-                return true;
-            }
-
-            if (!string.IsNullOrWhiteSpace(query.NameLessThan))
-            {
-                Logger.Debug("Query requires post-filtering due to NameLessThan");
-                return true;
-            }
-
-            if (!string.IsNullOrWhiteSpace(query.NameStartsWith))
-            {
-                Logger.Debug("Query requires post-filtering due to NameStartsWith");
-                return true;
-            }
-
-            if (!string.IsNullOrWhiteSpace(query.NameStartsWithOrGreater))
-            {
-                Logger.Debug("Query requires post-filtering due to NameStartsWithOrGreater");
-                return true;
-            }
-
             if (query.AirDays.Length > 0)
             {
                 Logger.Debug("Query requires post-filtering due to AirDays");
@@ -1635,7 +1606,7 @@ namespace MediaBrowser.Controller.Entities
 
                 var isUnplayed = true;
 
-                var itemUserData = UserDataManager.GetUserData(user.Id, child.GetUserDataKey());
+                var itemUserData = UserDataManager.GetUserData(user, child);
 
                 // Incrememt totalPercentPlayed
                 if (itemUserData != null)

+ 4 - 3
MediaBrowser.Controller/Entities/Game.cs

@@ -76,15 +76,16 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         public List<string> MultiPartGameFiles { get; set; }
 
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
+            var list = base.GetUserDataKeys();
             var id = this.GetProviderId(MetadataProviders.Gamesdb);
 
             if (!string.IsNullOrEmpty(id))
             {
-                return "Game-Gamesdb-" + id;
+                list.Insert(0, "Game-Gamesdb-" + id);
             }
-            return base.CreateUserDataKey();
+            return list;
         }
 
         public override IEnumerable<string> GetDeletePaths()

+ 5 - 6
MediaBrowser.Controller/Entities/GameGenre.cs

@@ -7,13 +7,12 @@ namespace MediaBrowser.Controller.Entities
 {
     public class GameGenre : BaseItem, IItemByName
     {
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            return "GameGenre-" + Name;
+            var list = base.GetUserDataKeys();
+
+            list.Insert(0, "GameGenre-" + Name);
+            return list;
         }
 
         /// <summary>

+ 6 - 7
MediaBrowser.Controller/Entities/GameSystem.cs

@@ -2,6 +2,7 @@
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Configuration;
 using System;
+using System.Collections.Generic;
 using MediaBrowser.Model.Users;
 
 namespace MediaBrowser.Controller.Entities
@@ -31,17 +32,15 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The game system.</value>
         public string GameSystemName { get; set; }
 
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
+            var list = base.GetUserDataKeys();
+
             if (!string.IsNullOrEmpty(GameSystemName))
             {
-                return "GameSystem-" + GameSystemName;
+                list.Insert(0, "GameSystem-" + GameSystemName);
             }
-            return base.CreateUserDataKey();
+            return list;
         }
 
         protected override bool GetBlockUnratedValue(UserPolicy config)

+ 5 - 6
MediaBrowser.Controller/Entities/Genre.cs

@@ -11,13 +11,12 @@ namespace MediaBrowser.Controller.Entities
     /// </summary>
     public class Genre : BaseItem, IItemByName
     {
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            return "Genre-" + Name;
+            var list = base.GetUserDataKeys();
+
+            list.Insert(0, "Genre-" + Name);
+            return list;
         }
 
         /// <summary>

+ 6 - 1
MediaBrowser.Controller/Entities/IHasUserData.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Dto;
+using System.Collections.Generic;
+using MediaBrowser.Model.Dto;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -13,6 +14,8 @@ namespace MediaBrowser.Controller.Entities
         /// <returns>System.String.</returns>
         string GetUserDataKey();
 
+        List<string> GetUserDataKeys();
+
         /// <summary>
         /// Fills the user data dto values.
         /// </summary>
@@ -20,5 +23,7 @@ namespace MediaBrowser.Controller.Entities
         /// <param name="userData">The user data.</param>
         /// <param name="user">The user.</param>
         void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user);
+
+        bool EnableRememberingTrackSelections { get; }
     }
 }

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

@@ -46,6 +46,7 @@ namespace MediaBrowser.Controller.Entities
         public string NameLessThan { get; set; }
         public string NameContains { get; set; }
 
+        public string PresentationUniqueKey { get; set; }
         public string Path { get; set; }
         
         public string Person { get; set; }

+ 0 - 30
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -18,8 +18,6 @@ namespace MediaBrowser.Controller.Entities.Movies
     {
         public List<Guid> SpecialFeatureIds { get; set; }
 
-        public string OriginalTitle { get; set; }
-
         public List<Guid> ThemeSongIds { get; set; }
         public List<Guid> ThemeVideoIds { get; set; }
         public List<string> ProductionLocations { get; set; }
@@ -77,34 +75,6 @@ namespace MediaBrowser.Controller.Entities.Movies
             get { return TmdbCollectionName; }
             set { TmdbCollectionName = value; }
         }
-        
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
-        {
-            var key = GetMovieUserDataKey(this);
-
-            if (string.IsNullOrWhiteSpace(key))
-            {
-                key = base.CreateUserDataKey();
-            }
-
-            return key;
-        }
-
-        public static string GetMovieUserDataKey(BaseItem movie)
-        {
-            var key = movie.GetProviderId(MetadataProviders.Tmdb);
-
-            if (string.IsNullOrWhiteSpace(key))
-            {
-                key = movie.GetProviderId(MetadataProviders.Imdb);
-            }
-
-            return key;
-        }
 
         protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
         {

+ 0 - 9
MediaBrowser.Controller/Entities/MusicVideo.cs

@@ -44,15 +44,6 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
-        {
-            return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.CreateUserDataKey();
-        }
-
         public override UnratedItem GetBlockUnratedType()
         {
             return UnratedItem.Music;

+ 5 - 6
MediaBrowser.Controller/Entities/Person.cs

@@ -18,13 +18,12 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The place of birth.</value>
         public string PlaceOfBirth { get; set; }
 
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            return "Person-" + Name;
+            var list = base.GetUserDataKeys();
+
+            list.Insert(0, "Person-" + Name);
+            return list;
         }
 
         public PersonLookupInfo GetLookupInfo()

+ 5 - 6
MediaBrowser.Controller/Entities/Studio.cs

@@ -10,13 +10,12 @@ namespace MediaBrowser.Controller.Entities
     /// </summary>
     public class Studio : BaseItem, IItemByName, IHasTags
     {
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            return "Studio-" + Name;
+            var list = base.GetUserDataKeys();
+
+            list.Insert(0, "Studio-" + Name);
+            return list;
         }
 
         /// <summary>

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

@@ -58,60 +58,48 @@ namespace MediaBrowser.Controller.Entities.TV
         {
             get
             {
-                return AirsAfterSeasonNumber ?? AirsBeforeSeasonNumber ?? PhysicalSeasonNumber;
+                return AirsAfterSeasonNumber ?? AirsBeforeSeasonNumber ?? ParentIndexNumber;
             }
         }
 
         [IgnoreDataMember]
-        public int? PhysicalSeasonNumber
+        public override Folder LatestItemsIndexContainer
         {
             get
             {
-                var value = ParentIndexNumber;
-
-                if (value.HasValue)
-                {
-                    return value;
-                }
-
-                var season = Season;
-
-                return season != null ? season.IndexNumber : null;
+                return Series;
             }
         }
 
         [IgnoreDataMember]
-        public override Folder LatestItemsIndexContainer
+        public override Guid? DisplayParentId
         {
             get
             {
-                return Series;
+                return SeasonId;
             }
         }
 
         [IgnoreDataMember]
-        public override Guid? DisplayParentId
+        protected override bool EnableDefaultVideoUserDataKeys
         {
             get
             {
-                return SeasonId;
+                return false;
             }
         }
 
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            var series = Series;
+            var list = base.GetUserDataKeys();
 
+            var series = Series;
             if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
             {
-                return series.GetUserDataKey() + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000");
+                list.InsertRange(0, series.GetUserDataKeys().Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000")));
             }
 
-            return base.CreateUserDataKey();
+            return list;
         }
 
         /// <summary>
@@ -310,6 +298,19 @@ namespace MediaBrowser.Controller.Entities.TV
                 Logger.ErrorException("Error in FillMissingEpisodeNumbersFromPath. Episode: {0}", ex, Path ?? Name ?? Id.ToString());
             }
 
+            if (!ParentIndexNumber.HasValue)
+            {
+                var season = Season;
+                if (season != null)
+                {
+                    if (season.ParentIndexNumber.HasValue)
+                    {
+                        ParentIndexNumber = season.ParentIndexNumber;
+                        hasChanges = true;
+                    }
+                }
+            }
+
             return hasChanges;
         }
     }

+ 29 - 13
MediaBrowser.Controller/Entities/TV/Season.cs

@@ -53,19 +53,17 @@ namespace MediaBrowser.Controller.Entities.TV
             };
         }
 
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            if (Series != null)
+            var list = base.GetUserDataKeys();
+
+            var series = Series;
+            if (series != null)
             {
-                var seasonNo = IndexNumber ?? 0;
-                return Series.GetUserDataKey() + seasonNo.ToString("000");
+                list.InsertRange(0, series.GetUserDataKeys().Select(i => i + (IndexNumber ?? 0).ToString("000")));
             }
 
-            return base.CreateUserDataKey();
+            return list;
         }
 
         /// <summary>
@@ -94,6 +92,24 @@ namespace MediaBrowser.Controller.Entities.TV
             }
         }
 
+        [IgnoreDataMember]
+        public override string PresentationUniqueKey
+        {
+            get
+            {
+                if (IndexNumber.HasValue)
+                {
+                    var series = Series;
+                    if (series != null)
+                    {
+                        return series.PresentationUniqueKey + "-" + (IndexNumber ?? 0).ToString("000");
+                    }
+                }
+
+                return base.PresentationUniqueKey;
+            }
+        }
+
         /// <summary>
         /// Creates the name of the sort.
         /// </summary>
@@ -171,16 +187,16 @@ namespace MediaBrowser.Controller.Entities.TV
 
         public IEnumerable<Episode> GetEpisodes(User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
         {
-            var episodes = GetRecursiveChildren(user)
-                .OfType<Episode>();
-
             var series = Series;
 
             if (IndexNumber.HasValue && series != null)
             {
-                return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes);
+                return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes);
             }
 
+            var episodes = GetRecursiveChildren(user)
+                .OfType<Episode>();
+
             if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
             {
                 var seasonNumber = IndexNumber;

+ 84 - 29
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -19,8 +19,6 @@ namespace MediaBrowser.Controller.Entities.TV
     {
         public List<Guid> SpecialFeatureIds { get; set; }
 
-        public string OriginalTitle { get; set; }
-
         public int? AnimeSeriesIndex { get; set; }
 
         public Series()
@@ -93,25 +91,33 @@ namespace MediaBrowser.Controller.Entities.TV
             }
         }
 
+        [IgnoreDataMember]
+        public override string PresentationUniqueKey
+        {
+            get { return GetUserDataKeys().First(); }
+        }
+
         /// <summary>
         /// Gets the user data key.
         /// </summary>
         /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            var key = this.GetProviderId(MetadataProviders.Tvdb);
+            var list = base.GetUserDataKeys();
 
-            if (string.IsNullOrWhiteSpace(key))
+            var key = this.GetProviderId(MetadataProviders.Imdb);
+            if (!string.IsNullOrWhiteSpace(key))
             {
-                key = this.GetProviderId(MetadataProviders.Imdb);
+                list.Insert(0, key);
             }
 
-            if (string.IsNullOrWhiteSpace(key))
+            key = this.GetProviderId(MetadataProviders.Tvdb);
+            if (!string.IsNullOrWhiteSpace(key))
             {
-                key = base.CreateUserDataKey();
+                list.Insert(0, key);
             }
 
-            return key;
+            return list;
         }
 
         /// <summary>
@@ -128,8 +134,8 @@ namespace MediaBrowser.Controller.Entities.TV
         // Studio, Genre and Rating will all be the same so makes no sense to index by these
         protected override IEnumerable<string> GetIndexByOptions()
         {
-            return new List<string> {            
-                {"None"}, 
+            return new List<string> {
+                {"None"},
                 {"Performer"},
                 {"Director"},
                 {"Year"},
@@ -185,8 +191,28 @@ namespace MediaBrowser.Controller.Entities.TV
 
         public IEnumerable<Season> GetSeasons(User user, bool includeMissingSeasons, bool includeVirtualUnaired)
         {
-            var seasons = base.GetChildren(user, true)
-                .OfType<Season>();
+            var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user)
+            {
+                PresentationUniqueKey = PresentationUniqueKey,
+                IncludeItemTypes = new[] { typeof(Series).Name }
+            });
+
+            IEnumerable<Season> seasons;
+
+            if (seriesIds.Count > 1)
+            {
+                seasons = LibraryManager.GetItemList(new InternalItemsQuery(user)
+                {
+                    AncestorIds = seriesIds.Select(i => i.ToString("N")).ToArray(),
+                    IncludeItemTypes = new[] { typeof(Season).Name },
+                    SortBy = new[] { ItemSortBy.SortName }
+
+                }).OfType<Season>();
+            }
+            else
+            {
+                seasons = base.GetChildren(user, true).OfType<Season>();
+            }
 
             if (!includeMissingSeasons && !includeVirtualUnaired)
             {
@@ -204,9 +230,7 @@ namespace MediaBrowser.Controller.Entities.TV
                 }
             }
 
-            return LibraryManager
-                .Sort(seasons, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
-                .Cast<Season>();
+            return seasons;
         }
 
         public IEnumerable<Episode> GetEpisodes(User user)
@@ -257,7 +281,7 @@ namespace MediaBrowser.Controller.Entities.TV
             // Refresh current item
             await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
 
-            // Refresh TV
+            // Refresh seasons
             foreach (var item in seasons)
             {
                 cancellationToken.ThrowIfCancellationRequested();
@@ -270,12 +294,30 @@ namespace MediaBrowser.Controller.Entities.TV
                 progress.Report(percent * 100);
             }
 
-            // Refresh all non-songs
+            // Refresh episodes and other children
             foreach (var item in otherItems)
             {
                 cancellationToken.ThrowIfCancellationRequested();
 
-                await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+                var skipItem = false;
+
+                var episode = item as Episode;
+
+                if (episode != null
+                    && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.FullRefresh
+                    && !refreshOptions.ReplaceAllMetadata
+                    && episode.IsMissingEpisode
+                    && episode.LocationType == Model.Entities.LocationType.Virtual
+                    && episode.PremiereDate.HasValue
+                    && (DateTime.UtcNow - episode.PremiereDate.Value).TotalDays > 30)
+                {
+                    skipItem = true;
+                }
+
+                if (!skipItem)
+                {
+                    await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+                }
 
                 numComplete++;
                 double percent = numComplete;
@@ -297,18 +339,31 @@ namespace MediaBrowser.Controller.Entities.TV
 
         public IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
         {
-            return GetEpisodes(user, seasonNumber, includeMissingEpisodes, includeVirtualUnairedEpisodes,
-                new List<Episode>());
-        }
+            var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user)
+            {
+                PresentationUniqueKey = PresentationUniqueKey,
+                IncludeItemTypes = new[] { typeof(Series).Name }
+            });
 
-        internal IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> additionalEpisodes)
-        {
-            var episodes = GetRecursiveChildren(user, i => i is Episode)
-                .Cast<Episode>();
+            IEnumerable<Episode> episodes;
 
-            episodes = FilterEpisodesBySeason(episodes, seasonNumber, DisplaySpecialsWithSeasons);
+            if (seriesIds.Count > 1)
+            {
+                episodes = LibraryManager.GetItemList(new InternalItemsQuery(user)
+                {
+                    AncestorIds = seriesIds.Select(i => i.ToString("N")).ToArray(),
+                    IncludeItemTypes = new[] { typeof(Episode).Name },
+                    SortBy = new[] { ItemSortBy.SortName }
 
-            episodes = episodes.Concat(additionalEpisodes).Distinct();
+                }).OfType<Episode>();
+            }
+            else
+            {
+                episodes = GetRecursiveChildren(user, i => i is Episode)
+                    .Cast<Episode>();
+            }
+
+            episodes = FilterEpisodesBySeason(episodes, seasonNumber, DisplaySpecialsWithSeasons);
 
             if (!includeMissingEpisodes)
             {
@@ -336,7 +391,7 @@ namespace MediaBrowser.Controller.Entities.TV
         {
             if (!includeSpecials || seasonNumber < 1)
             {
-                return episodes.Where(i => (i.PhysicalSeasonNumber ?? -1) == seasonNumber);
+                return episodes.Where(i => (i.ParentIndexNumber ?? -1) == seasonNumber);
             }
 
             return episodes.Where(i =>

+ 1 - 21
MediaBrowser.Controller/Entities/Trailer.cs

@@ -11,7 +11,7 @@ namespace MediaBrowser.Controller.Entities
     /// <summary>
     /// Class Trailer
     /// </summary>
-    public class Trailer : Video, IHasCriticRating, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo<TrailerInfo>
+    public class Trailer : Video, IHasCriticRating, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasOriginalTitle, IHasLookupInfo<TrailerInfo>
     {
         public List<string> ProductionLocations { get; set; }
 
@@ -56,26 +56,6 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The revenue.</value>
         public double? Revenue { get; set; }
 
-        protected override string CreateUserDataKey()
-        {
-            var key = Movie.GetMovieUserDataKey(this);
-
-            if (!string.IsNullOrWhiteSpace(key))
-            {
-                key = key + "-trailer";
-
-                // Make sure different trailers have their own data.
-                if (RunTimeTicks.HasValue)
-                {
-                    key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
-                }
-
-                return key;
-            }
-
-            return base.CreateUserDataKey();
-        }
-
         public override UnratedItem GetBlockUnratedType()
         {
             return UnratedItem.Trailer;

+ 5 - 5
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -348,7 +348,7 @@ namespace MediaBrowser.Controller.Entities
                 .Where(i => !i.IsFolder)
                 .OfType<IHasAlbumArtist>();
 
-            var artists = _libraryManager.GetAlbumArtists(items).Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite);
+            var artists = _libraryManager.GetAlbumArtists(items).Where(i => _userDataManager.GetUserData(user, i).IsFavorite);
 
             return GetResult(artists, parent, query);
         }
@@ -1218,7 +1218,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (query.IsLiked.HasValue)
             {
-                userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey());
+                userData = userData ?? userDataManager.GetUserData(user, item);
 
                 if (!userData.Likes.HasValue || userData.Likes != query.IsLiked.Value)
                 {
@@ -1228,7 +1228,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (query.IsFavoriteOrLiked.HasValue)
             {
-                userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey());
+                userData = userData ?? userDataManager.GetUserData(user, item);
                 var isFavoriteOrLiked = userData.IsFavorite || (userData.Likes ?? false);
 
                 if (isFavoriteOrLiked != query.IsFavoriteOrLiked.Value)
@@ -1239,7 +1239,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (query.IsFavorite.HasValue)
             {
-                userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey());
+                userData = userData ?? userDataManager.GetUserData(user, item);
 
                 if (userData.IsFavorite != query.IsFavorite.Value)
                 {
@@ -1249,7 +1249,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (query.IsResumable.HasValue)
             {
-                userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey());
+                userData = userData ?? userDataManager.GetUserData(user, item);
                 var isResumable = userData.PlaybackPositionTicks > 0;
 
                 if (isResumable != query.IsResumable.Value)

+ 103 - 28
MediaBrowser.Controller/Entities/Video.cs

@@ -44,6 +44,20 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        public override string PresentationUniqueKey
+        {
+            get
+            {
+                if (PrimaryVersionId.HasValue)
+                {
+                    return PrimaryVersionId.Value.ToString("N");
+                }
+
+                return base.PresentationUniqueKey;
+            }
+        }
+
         public long? Size { get; set; }
         public string Container { get; set; }
         public int? TotalBitrate { get; set; }
@@ -90,6 +104,14 @@ namespace MediaBrowser.Controller.Entities
         {
             get
             {
+                if (PrimaryVersionId.HasValue)
+                {
+                    var item = LibraryManager.GetItemById(PrimaryVersionId.Value) as Video;
+                    if (item != null)
+                    {
+                        return item.MediaSourceCount;
+                    }
+                }
                 return LinkedAlternateVersions.Count + LocalAlternateVersions.Count + 1;
             }
         }
@@ -131,42 +153,65 @@ namespace MediaBrowser.Controller.Entities
             return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
         }
 
-        protected override string CreateUserDataKey()
+        [IgnoreDataMember]
+        protected virtual bool EnableDefaultVideoUserDataKeys
         {
-            if (ExtraType.HasValue)
+            get
             {
-                var key = this.GetProviderId(MetadataProviders.Imdb) ?? this.GetProviderId(MetadataProviders.Tmdb);
+                return true;
+            }
+        }
+
+        public override List<string> GetUserDataKeys()
+        {
+            var list = base.GetUserDataKeys();
 
-                if (!string.IsNullOrWhiteSpace(key))
+            if (EnableDefaultVideoUserDataKeys)
+            {
+                if (ExtraType.HasValue)
                 {
-                    key = key + "-" + ExtraType.ToString().ToLower();
+                    var key = this.GetProviderId(MetadataProviders.Tmdb);
+                    if (!string.IsNullOrWhiteSpace(key))
+                    {
+                        list.Insert(0, GetUserDataKey(key));
+                    }
 
-                    // Make sure different trailers have their own data.
-                    if (RunTimeTicks.HasValue)
+                    key = this.GetProviderId(MetadataProviders.Imdb);
+                    if (!string.IsNullOrWhiteSpace(key))
                     {
-                        key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
+                        list.Insert(0, GetUserDataKey(key));
+                    }
+                }
+                else
+                {
+                    var key = this.GetProviderId(MetadataProviders.Imdb);
+                    if (!string.IsNullOrWhiteSpace(key))
+                    {
+                        list.Insert(0, key);
                     }
 
-                    return key;
+                    key = this.GetProviderId(MetadataProviders.Tmdb);
+                    if (!string.IsNullOrWhiteSpace(key))
+                    {
+                        list.Insert(0, key);
+                    }
                 }
             }
 
-            return base.CreateUserDataKey();
+            return list;
         }
 
-        /// <summary>
-        /// Gets the linked children.
-        /// </summary>
-        /// <returns>IEnumerable{BaseItem}.</returns>
-        public IEnumerable<Video> GetAlternateVersions()
+        private string GetUserDataKey(string providerId)
         {
-            var filesWithinSameDirectory = GetLocalAlternateVersionIds()
-                .Select(i => LibraryManager.GetItemById(i))
-                .Where(i => i != null)
-                .OfType<Video>();
+            var key = providerId + "-" + ExtraType.ToString().ToLower();
 
-            return filesWithinSameDirectory.Concat(GetLinkedAlternateVersions())
-                .OrderBy(i => i.SortName);
+            // Make sure different trailers have their own data.
+            if (RunTimeTicks.HasValue)
+            {
+                key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
+            }
+
+            return key;
         }
 
         public IEnumerable<Video> GetLinkedAlternateVersions()
@@ -317,6 +362,11 @@ namespace MediaBrowser.Controller.Entities
                 {
                     return false;
                 }
+
+                if (newAsVideo.VideoType != VideoType)
+                {
+                    return false;
+                }
             }
 
             return base.IsValidFromResolver(newItem);
@@ -463,6 +513,36 @@ namespace MediaBrowser.Controller.Entities
             }).FirstOrDefault();
         }
 
+        private List<Tuple<Video, MediaSourceType>> GetAllVideosForMediaSources()
+        {
+            var list = new List<Tuple<Video, MediaSourceType>>();
+
+            list.Add(new Tuple<Video, MediaSourceType>(this, MediaSourceType.Default));
+            list.AddRange(GetLinkedAlternateVersions().Select(i => new Tuple<Video, MediaSourceType>(i, MediaSourceType.Grouping)));
+
+            if (PrimaryVersionId.HasValue)
+            {
+                var primary = LibraryManager.GetItemById(PrimaryVersionId.Value) as Video;
+                if (primary != null)
+                {
+                    var existingIds = list.Select(i => i.Item1.Id).ToList();
+                    list.Add(new Tuple<Video, MediaSourceType>(primary, MediaSourceType.Grouping));
+                    list.AddRange(primary.GetLinkedAlternateVersions().Where(i => !existingIds.Contains(i.Id)).Select(i => new Tuple<Video, MediaSourceType>(i, MediaSourceType.Grouping)));
+                }
+            }
+
+            var localAlternates = list
+                .SelectMany(i => i.Item1.GetLocalAlternateVersionIds())
+                .Select(LibraryManager.GetItemById)
+                .Where(i => i != null)
+                .OfType<Video>()
+                .ToList();
+
+            list.AddRange(localAlternates.Select(i => new Tuple<Video, MediaSourceType>(i, MediaSourceType.Default)));
+
+            return list;
+        }
+
         public virtual IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
         {
             if (SourceType == SourceType.Channel)
@@ -481,13 +561,8 @@ namespace MediaBrowser.Controller.Entities
                 };
             }
 
-            var item = this;
-
-            var result = item.GetAlternateVersions()
-                .Select(i => GetVersionInfo(enablePathSubstitution, i, MediaSourceType.Grouping))
-                .ToList();
-
-            result.Add(GetVersionInfo(enablePathSubstitution, item, MediaSourceType.Default));
+            var list = GetAllVideosForMediaSources();
+            var result = list.Select(i => GetVersionInfo(enablePathSubstitution, i.Item1, i.Item2)).ToList();
 
             return result.OrderBy(i =>
             {

+ 5 - 6
MediaBrowser.Controller/Entities/Year.cs

@@ -11,13 +11,12 @@ namespace MediaBrowser.Controller.Entities
     /// </summary>
     public class Year : BaseItem, IItemByName
     {
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            return "Year-" + Name;
+            var list = base.GetUserDataKeys();
+
+            list.Insert(0, "Year-" + Name);
+            return list;
         }
 
         /// <summary>

+ 17 - 15
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -25,7 +25,7 @@ namespace MediaBrowser.Controller.Library
         /// <param name="fileInfo">The file information.</param>
         /// <param name="parent">The parent.</param>
         /// <returns>BaseItem.</returns>
-        BaseItem ResolvePath(FileSystemMetadata fileInfo, 
+        BaseItem ResolvePath(FileSystemMetadata fileInfo,
             Folder parent = null);
 
         /// <summary>
@@ -36,9 +36,9 @@ namespace MediaBrowser.Controller.Library
         /// <param name="parent">The parent.</param>
         /// <param name="collectionType">Type of the collection.</param>
         /// <returns>List{``0}.</returns>
-        IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, 
+        IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files,
             IDirectoryService directoryService,
-            Folder parent, string 
+            Folder parent, string
             collectionType = null);
 
         /// <summary>
@@ -59,8 +59,8 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         /// <param name="path">The path.</param>
         /// <returns>BaseItem.</returns>
-        BaseItem FindByPath(string path);
-        
+        BaseItem FindByPath(string path, bool? isFolder);
+
         /// <summary>
         /// Gets the artist.
         /// </summary>
@@ -156,7 +156,7 @@ namespace MediaBrowser.Controller.Library
         /// <param name="id">The identifier.</param>
         /// <returns>BaseItem.</returns>
         BaseItem GetMemoryItemById(Guid id);
-        
+
         /// <summary>
         /// Gets the intros.
         /// </summary>
@@ -243,6 +243,8 @@ namespace MediaBrowser.Controller.Library
         /// <returns>BaseItem.</returns>
         BaseItem RetrieveItem(Guid id);
 
+        bool IsScanRunning { get; }
+
         /// <summary>
         /// Occurs when [item added].
         /// </summary>
@@ -290,7 +292,7 @@ namespace MediaBrowser.Controller.Library
         /// <param name="path">The path.</param>
         /// <returns>System.String.</returns>
         string GetConfiguredContentType(string path);
-        
+
         /// <summary>
         /// Normalizes the root path list.
         /// </summary>
@@ -332,8 +334,8 @@ namespace MediaBrowser.Controller.Library
         Task<UserView> GetNamedView(User user,
             string name,
             string parentId,
-            string viewType, 
-            string sortName, 
+            string viewType,
+            string sortName,
             CancellationToken cancellationToken);
 
         /// <summary>
@@ -346,8 +348,8 @@ namespace MediaBrowser.Controller.Library
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;UserView&gt;.</returns>
         Task<UserView> GetNamedView(User user,
-            string name, 
-            string viewType, 
+            string name,
+            string viewType,
             string sortName,
             CancellationToken cancellationToken);
 
@@ -393,7 +395,7 @@ namespace MediaBrowser.Controller.Library
           string viewType,
           string sortName,
           CancellationToken cancellationToken);
-        
+
         /// <summary>
         /// Determines whether [is video file] [the specified path].
         /// </summary>
@@ -477,14 +479,14 @@ namespace MediaBrowser.Controller.Library
         /// <param name="query">The query.</param>
         /// <returns>List&lt;PersonInfo&gt;.</returns>
         List<PersonInfo> GetPeople(InternalPeopleQuery query);
-        
+
         /// <summary>
         /// Gets the people items.
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>List&lt;Person&gt;.</returns>
         List<Person> GetPeopleItems(InternalPeopleQuery query);
-        
+
         /// <summary>
         /// Gets all people names.
         /// </summary>
@@ -559,7 +561,7 @@ namespace MediaBrowser.Controller.Library
         /// <param name="query">The query.</param>
         /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
         QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query);
-        
+
         /// <summary>
         /// Ignores the file.
         /// </summary>

+ 5 - 0
MediaBrowser.Controller/Library/IUserDataManager.cs

@@ -45,6 +45,11 @@ namespace MediaBrowser.Controller.Library
         /// <returns>Task{UserItemData}.</returns>
         UserItemData GetUserData(Guid userId, string key);
 
+        UserItemData GetUserData(IHasUserData user, IHasUserData item);
+
+        UserItemData GetUserData(string userId, IHasUserData item);
+        UserItemData GetUserData(Guid userId, IHasUserData item);
+
         /// <summary>
         /// Gets the user data dto.
         /// </summary>

+ 1 - 1
MediaBrowser.Controller/Library/TVUtils.cs

@@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Library
         /// <summary>
         /// The banner URL
         /// </summary>
-        public static readonly string BannerUrl = "http://www.thetvdb.com/banners/";
+        public static readonly string BannerUrl = "https://www.thetvdb.com/banners/";
 
         /// <summary>
         /// Gets the air days.

+ 2 - 5
MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs

@@ -1,6 +1,7 @@
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Entities;
 using System;
+using System.Collections.Generic;
 
 namespace MediaBrowser.Controller.Library
 {
@@ -15,11 +16,7 @@ namespace MediaBrowser.Controller.Library
         /// <value>The user id.</value>
         public Guid UserId { get; set; }
 
-        /// <summary>
-        /// Gets or sets the key.
-        /// </summary>
-        /// <value>The key.</value>
-        public string Key { get; set; }
+        public List<string> Keys { get; set; }
 
         /// <summary>
         /// Gets or sets the save reason.

+ 2 - 0
MediaBrowser.Controller/LiveTv/ITunerHost.cs

@@ -46,6 +46,8 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
         Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
+
+        string ApplyDuration(string streamPath, TimeSpan duration);
     }
     public interface IConfigurableTunerHost
     {

+ 0 - 11
MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs

@@ -45,17 +45,6 @@ namespace MediaBrowser.Controller.LiveTv
             set { }
         }
 
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
-        {
-            var name = GetClientTypeName();
-
-            return name + "-" + Name + (EpisodeTitle ?? string.Empty);
-        }
-
         /// <summary>
         /// Gets a value indicating whether this instance is owned item.
         /// </summary>

+ 14 - 6
MediaBrowser.Controller/LiveTv/LiveTvChannel.cs

@@ -11,13 +11,12 @@ namespace MediaBrowser.Controller.LiveTv
 {
     public class LiveTvChannel : BaseItem, IHasMediaSources
     {
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            return GetClientTypeName() + "-" + Name;
+            var list = base.GetUserDataKeys();
+
+            list.Insert(0, GetClientTypeName() + "-" + Name);
+            return list;
         }
 
         public override UnratedItem GetBlockUnratedType()
@@ -45,6 +44,15 @@ namespace MediaBrowser.Controller.LiveTv
             set { }
         }
 
+        [IgnoreDataMember]
+        public override bool EnableRememberingTrackSelections
+        {
+            get
+            {
+                return false;
+            }
+        }
+
         /// <summary>
         /// Gets or sets the number.
         /// </summary>

+ 16 - 12
MediaBrowser.Controller/LiveTv/LiveTvProgram.cs

@@ -4,36 +4,40 @@ using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.LiveTv;
 using System;
+using System.Collections.Generic;
 using System.Runtime.Serialization;
+using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Controller.LiveTv
 {
     public class LiveTvProgram : BaseItem, IHasLookupInfo<LiveTvProgramLookupInfo>, IHasStartDate, IHasProgramAttributes
     {
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
+        public override List<string> GetUserDataKeys()
         {
-            if (IsMovie)
+            var list = base.GetUserDataKeys();
+
+            if (!IsSeries)
             {
-                var key = Movie.GetMovieUserDataKey(this);
+                var key = this.GetProviderId(MetadataProviders.Imdb);
+                if (!string.IsNullOrWhiteSpace(key))
+                {
+                    list.Insert(0, key);
+                }
 
+                key = this.GetProviderId(MetadataProviders.Tmdb);
                 if (!string.IsNullOrWhiteSpace(key))
                 {
-                    return key;
+                    list.Insert(0, key);
                 }
             }
-
-            if (IsSeries && !string.IsNullOrWhiteSpace(EpisodeTitle))
+            else if (!string.IsNullOrWhiteSpace(EpisodeTitle))
             {
                 var name = GetClientTypeName();
 
-                return name + "-" + Name + (EpisodeTitle ?? string.Empty);
+                list.Insert(0, name + "-" + Name + (EpisodeTitle ?? string.Empty));
             }
 
-            return base.CreateUserDataKey();
+            return list;
         }
 
         [IgnoreDataMember]

+ 0 - 26
MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs

@@ -45,32 +45,6 @@ namespace MediaBrowser.Controller.LiveTv
             set { }
         }
 
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        protected override string CreateUserDataKey()
-        {
-            if (IsMovie)
-            {
-                var key = Movie.GetMovieUserDataKey(this);
-
-                if (!string.IsNullOrWhiteSpace(key))
-                {
-                    return key;
-                }
-            }
-
-            if (IsSeries && !string.IsNullOrWhiteSpace(EpisodeTitle))
-            {
-                var name = GetClientTypeName();
-
-                return name + "-" + Name + (EpisodeTitle ?? string.Empty);
-            }
-
-            return base.CreateUserDataKey();
-        }
-
         [IgnoreDataMember]
         public override string MediaType
         {

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

@@ -4,13 +4,14 @@ using System;
 using System.IO;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Controller.MediaEncoding
 {
     /// <summary>
     /// Interface IMediaEncoder
     /// </summary>
-    public interface IMediaEncoder
+    public interface IMediaEncoder : ITranscoderSupport
     {
         /// <summary>
         /// Gets the encoder path.

+ 0 - 6
MediaBrowser.Controller/Notifications/INotificationsRepository.cs

@@ -19,12 +19,6 @@ namespace MediaBrowser.Controller.Notifications
         /// Occurs when [notifications marked read].
         /// </summary>
         event EventHandler<NotificationReadEventArgs> NotificationsMarkedRead;
-
-        /// <summary>
-        /// Opens the connection to the repository
-        /// </summary>
-        /// <returns>Task.</returns>
-        Task Initialize();
         
         /// <summary>
         /// Gets the notifications.

+ 0 - 6
MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs

@@ -11,12 +11,6 @@ namespace MediaBrowser.Controller.Persistence
     /// </summary>
     public interface IDisplayPreferencesRepository : IRepository
     {
-        /// <summary>
-        /// Opens the connection to the repository
-        /// </summary>
-        /// <returns>Task.</returns>
-        Task Initialize();
-
         /// <summary>
         /// Saves display preferences for an item
         /// </summary>

+ 0 - 6
MediaBrowser.Controller/Persistence/IItemRepository.cs

@@ -13,12 +13,6 @@ namespace MediaBrowser.Controller.Persistence
     /// </summary>
     public interface IItemRepository : IRepository
     {
-        /// <summary>
-        /// Opens the connection to the repository
-        /// </summary>
-        /// <returns>Task.</returns>
-        Task Initialize();
-
         /// <summary>
         /// Saves an item
         /// </summary>

+ 0 - 6
MediaBrowser.Controller/Persistence/IUserDataRepository.cs

@@ -11,12 +11,6 @@ namespace MediaBrowser.Controller.Persistence
     /// </summary>
     public interface IUserDataRepository : IRepository
     {
-        /// <summary>
-        /// Opens the connection to the repository
-        /// </summary>
-        /// <returns>Task.</returns>
-        Task Initialize();
-
         /// <summary>
         /// Saves the user data.
         /// </summary>

+ 0 - 6
MediaBrowser.Controller/Providers/IProviderRepository.cs

@@ -21,11 +21,5 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         Task SaveMetadataStatus(MetadataStatus status, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Initializes this instance.
-        /// </summary>
-        /// <returns>Task.</returns>
-        Task Initialize();
     }
 }

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

@@ -12,6 +12,7 @@ using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using MediaBrowser.Controller.MediaEncoding;
 
 namespace MediaBrowser.Dlna.ContentDirectory
 {
@@ -27,6 +28,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
         private readonly IChannelManager _channelManager;
         private readonly IMediaSourceManager _mediaSourceManager;
         private readonly IUserViewManager _userViewManager;
+        private readonly Func<IMediaEncoder> _mediaEncoder;
 
         public ContentDirectory(IDlnaManager dlna,
             IUserDataManager userDataManager,
@@ -35,7 +37,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             IServerConfigurationManager config,
             IUserManager userManager,
             ILogger logger,
-            IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager)
+            IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, Func<IMediaEncoder> mediaEncoder)
             : base(logger, httpClient)
         {
             _dlna = dlna;
@@ -48,6 +50,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             _channelManager = channelManager;
             _mediaSourceManager = mediaSourceManager;
             _userViewManager = userViewManager;
+            _mediaEncoder = mediaEncoder;
         }
 
         private int SystemUpdateId
@@ -89,7 +92,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 _localization,
                 _channelManager,
                 _mediaSourceManager,
-                _userViewManager)
+                _userViewManager,
+                _mediaEncoder())
                 .ProcessControlRequest(request);
         }
 

+ 6 - 3
MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs

@@ -23,6 +23,7 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Xml;
+using MediaBrowser.Controller.MediaEncoding;
 
 namespace MediaBrowser.Dlna.ContentDirectory
 {
@@ -34,6 +35,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
         private readonly IServerConfigurationManager _config;
         private readonly User _user;
         private readonly IUserViewManager _userViewManager;
+        private readonly IMediaEncoder _mediaEncoder;
 
         private const string NS_DC = "http://purl.org/dc/elements/1.1/";
         private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
@@ -47,7 +49,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
         private readonly DeviceProfile _profile;
 
-        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager)
+        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder)
             : base(config, logger)
         {
             _libraryManager = libraryManager;
@@ -56,10 +58,11 @@ namespace MediaBrowser.Dlna.ContentDirectory
             _systemUpdateId = systemUpdateId;
             _channelManager = channelManager;
             _userViewManager = userViewManager;
+            _mediaEncoder = mediaEncoder;
             _profile = profile;
             _config = config;
 
-            _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, libraryManager);
+            _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, libraryManager, _mediaEncoder);
         }
 
         protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
@@ -108,7 +111,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
             var newbookmark = int.Parse(sparams["PosSecond"], _usCulture);
 
-            var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey());
+            var userdata = _userDataManager.GetUserData(user, item);
 
             userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
 

+ 6 - 3
MediaBrowser.Dlna/Didl/DidlBuilder.cs

@@ -19,6 +19,7 @@ using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Xml;
+using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Configuration;
 
 namespace MediaBrowser.Dlna.Didl
@@ -42,8 +43,9 @@ namespace MediaBrowser.Dlna.Didl
         private readonly IMediaSourceManager _mediaSourceManager;
         private readonly ILogger _logger;
         private readonly ILibraryManager _libraryManager;
+        private readonly IMediaEncoder _mediaEncoder;
 
-        public DidlBuilder(DeviceProfile profile, User user, IImageProcessor imageProcessor, string serverAddress, string accessToken, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, ILogger logger, ILibraryManager libraryManager)
+        public DidlBuilder(DeviceProfile profile, User user, IImageProcessor imageProcessor, string serverAddress, string accessToken, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, ILogger logger, ILibraryManager libraryManager, IMediaEncoder mediaEncoder)
         {
             _profile = profile;
             _imageProcessor = imageProcessor;
@@ -53,6 +55,7 @@ namespace MediaBrowser.Dlna.Didl
             _mediaSourceManager = mediaSourceManager;
             _logger = logger;
             _libraryManager = libraryManager;
+            _mediaEncoder = mediaEncoder;
             _accessToken = accessToken;
             _user = user;
         }
@@ -142,7 +145,7 @@ namespace MediaBrowser.Dlna.Didl
             {
                 var sources = _mediaSourceManager.GetStaticMediaSources(video, true, _user).ToList();
 
-                streamInfo = new StreamBuilder(GetStreamBuilderLogger(options)).BuildVideoItem(new VideoOptions
+                streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildVideoItem(new VideoOptions
                 {
                     ItemId = GetClientId(video),
                     MediaSources = sources,
@@ -385,7 +388,7 @@ namespace MediaBrowser.Dlna.Didl
             {
                 var sources = _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList();
 
-                streamInfo = new StreamBuilder(GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions
+                streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions
                {
                    ItemId = GetClientId(audio),
                    MediaSources = sources,

+ 6 - 2
MediaBrowser.Dlna/Main/DlnaEntryPoint.cs

@@ -14,6 +14,7 @@ using MediaBrowser.Dlna.Ssdp;
 using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Generic;
+using MediaBrowser.Controller.MediaEncoding;
 
 namespace MediaBrowser.Dlna.Main
 {
@@ -34,6 +35,7 @@ namespace MediaBrowser.Dlna.Main
         private readonly IUserDataManager _userDataManager;
         private readonly ILocalizationManager _localization;
         private readonly IMediaSourceManager _mediaSourceManager;
+        private readonly IMediaEncoder _mediaEncoder;
 
         private readonly SsdpHandler _ssdpHandler;
         private readonly IDeviceDiscovery _deviceDiscovery;
@@ -54,7 +56,7 @@ namespace MediaBrowser.Dlna.Main
             IUserDataManager userDataManager,
             ILocalizationManager localization,
             IMediaSourceManager mediaSourceManager,
-            ISsdpHandler ssdpHandler, IDeviceDiscovery deviceDiscovery)
+            ISsdpHandler ssdpHandler, IDeviceDiscovery deviceDiscovery, IMediaEncoder mediaEncoder)
         {
             _config = config;
             _appHost = appHost;
@@ -69,6 +71,7 @@ namespace MediaBrowser.Dlna.Main
             _localization = localization;
             _mediaSourceManager = mediaSourceManager;
             _deviceDiscovery = deviceDiscovery;
+            _mediaEncoder = mediaEncoder;
             _ssdpHandler = (SsdpHandler)ssdpHandler;
             _logger = logManager.GetLogger("Dlna");
         }
@@ -196,7 +199,8 @@ namespace MediaBrowser.Dlna.Main
                         _config,
                         _userDataManager,
                         _localization,
-                        _mediaSourceManager);
+                        _mediaSourceManager,
+                        _mediaEncoder);
 
                     _manager.Start();
                 }

+ 7 - 4
MediaBrowser.Dlna/PlayTo/PlayToController.cs

@@ -18,6 +18,7 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.MediaEncoding;
 
 namespace MediaBrowser.Dlna.PlayTo
 {
@@ -35,6 +36,7 @@ namespace MediaBrowser.Dlna.PlayTo
         private readonly ILocalizationManager _localization;
         private readonly IMediaSourceManager _mediaSourceManager;
         private readonly IConfigurationManager _config;
+        private readonly IMediaEncoder _mediaEncoder;
 
         private readonly IDeviceDiscovery _deviceDiscovery;
         private readonly string _serverAddress;
@@ -74,7 +76,7 @@ namespace MediaBrowser.Dlna.PlayTo
             get { return IsSessionActive; }
         }
 
-        public PlayToController(SessionInfo session, ISessionManager sessionManager, ILibraryManager libraryManager, ILogger logger, IDlnaManager dlnaManager, IUserManager userManager, IImageProcessor imageProcessor, string serverAddress, string accessToken, IDeviceDiscovery deviceDiscovery, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IConfigurationManager config)
+        public PlayToController(SessionInfo session, ISessionManager sessionManager, ILibraryManager libraryManager, ILogger logger, IDlnaManager dlnaManager, IUserManager userManager, IImageProcessor imageProcessor, string serverAddress, string accessToken, IDeviceDiscovery deviceDiscovery, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IConfigurationManager config, IMediaEncoder mediaEncoder)
         {
             _session = session;
             _sessionManager = sessionManager;
@@ -88,6 +90,7 @@ namespace MediaBrowser.Dlna.PlayTo
             _localization = localization;
             _mediaSourceManager = mediaSourceManager;
             _config = config;
+            _mediaEncoder = mediaEncoder;
             _accessToken = accessToken;
             _logger = logger;
             _creationTime = DateTime.UtcNow;
@@ -478,7 +481,7 @@ namespace MediaBrowser.Dlna.PlayTo
 
             playlistItem.StreamUrl = playlistItem.StreamInfo.ToDlnaUrl(_serverAddress, _accessToken);
 
-            var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _libraryManager)
+            var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _libraryManager, _mediaEncoder)
                 .GetItemDidl(_config.GetDlnaConfiguration(), item, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
 
             playlistItem.Didl = itemXml;
@@ -550,7 +553,7 @@ namespace MediaBrowser.Dlna.PlayTo
             {
                 return new PlaylistItem
                 {
-                    StreamInfo = new StreamBuilder(GetStreamBuilderLogger()).BuildVideoItem(new VideoOptions
+                    StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildVideoItem(new VideoOptions
                     {
                         ItemId = item.Id.ToString("N"),
                         MediaSources = mediaSources,
@@ -570,7 +573,7 @@ namespace MediaBrowser.Dlna.PlayTo
             {
                 return new PlaylistItem
                 {
-                    StreamInfo = new StreamBuilder(GetStreamBuilderLogger()).BuildAudioItem(new AudioOptions
+                    StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildAudioItem(new AudioOptions
                     {
                         ItemId = item.Id.ToString("N"),
                         MediaSources = mediaSources,

+ 6 - 2
MediaBrowser.Dlna/PlayTo/PlayToManager.cs

@@ -12,6 +12,7 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Net;
+using MediaBrowser.Controller.MediaEncoding;
 
 namespace MediaBrowser.Dlna.PlayTo
 {
@@ -32,11 +33,12 @@ namespace MediaBrowser.Dlna.PlayTo
 
         private readonly IDeviceDiscovery _deviceDiscovery;
         private readonly IMediaSourceManager _mediaSourceManager;
+        private readonly IMediaEncoder _mediaEncoder;
 
         private readonly List<string> _nonRendererUrls = new List<string>();
         private DateTime _lastRendererClear;
 
-        public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager)
+        public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder)
         {
             _logger = logger;
             _sessionManager = sessionManager;
@@ -51,6 +53,7 @@ namespace MediaBrowser.Dlna.PlayTo
             _userDataManager = userDataManager;
             _localization = localization;
             _mediaSourceManager = mediaSourceManager;
+            _mediaEncoder = mediaEncoder;
         }
 
         public void Start()
@@ -132,7 +135,8 @@ namespace MediaBrowser.Dlna.PlayTo
                         _userDataManager,
                         _localization,
                         _mediaSourceManager,
-                        _config);
+                        _config,
+                        _mediaEncoder);
 
                     controller.Init(device);
 

+ 18 - 17
MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs

@@ -109,30 +109,31 @@ namespace MediaBrowser.Dlna.Ssdp
 
 					var endPoint = new IPEndPoint(localIp, 1900);
 
-                    var socket = GetMulticastSocket(localIp, endPoint);
-
-                    var receiveBuffer = new byte[64000];
-
-                    CreateNotifier(localIp);
-
-                    while (!_tokenSource.IsCancellationRequested)
+                    using (var socket = GetMulticastSocket(localIp, endPoint))
                     {
-                        var receivedBytes = await socket.ReceiveAsync(receiveBuffer, 0, 64000);
+                        var receiveBuffer = new byte[64000];
+
+                        CreateNotifier(localIp);
 
-                        if (receivedBytes > 0)
+                        while (!_tokenSource.IsCancellationRequested)
                         {
-                            var args = SsdpHelper.ParseSsdpResponse(receiveBuffer);
-                            args.EndPoint = endPoint;
-                            args.LocalEndPoint = new IPEndPoint(localIp, 0);
+                            var receivedBytes = await socket.ReceiveAsync(receiveBuffer, 0, 64000);
 
-                            if (_ssdpHandler.IgnoreMessage(args, true))
+                            if (receivedBytes > 0)
                             {
-                                return;
-                            }
+                                var args = SsdpHelper.ParseSsdpResponse(receiveBuffer);
+                                args.EndPoint = endPoint;
+                                args.LocalEndPoint = new IPEndPoint(localIp, 0);
+
+                                if (_ssdpHandler.IgnoreMessage(args, true))
+                                {
+                                    return;
+                                }
 
-                            _ssdpHandler.LogMessageReceived(args, true);
+                                _ssdpHandler.LogMessageReceived(args, true);
 
-                            TryCreateDevice(args);
+                                TryCreateDevice(args);
+                            }
                         }
                     }
 

+ 3 - 3
MediaBrowser.LocalMetadata/BaseXmlProvider.cs

@@ -8,7 +8,7 @@ using CommonIO;
 
 namespace MediaBrowser.LocalMetadata
 {
-    public abstract class BaseXmlProvider<T> : ILocalMetadataProvider<T>, IHasChangeMonitor, IHasOrder
+    public abstract class BaseXmlProvider<T> : ILocalMetadataProvider<T>, IHasItemChangeMonitor, IHasOrder
         where T : IHasMetadata, new()
     {
         protected IFileSystem FileSystem;
@@ -56,7 +56,7 @@ namespace MediaBrowser.LocalMetadata
 
         protected abstract FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService);
 
-        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
+        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
         {
             var file = GetXmlFile(new ItemInfo(item), directoryService);
 
@@ -65,7 +65,7 @@ namespace MediaBrowser.LocalMetadata
                 return false;
             }
 
-            return file.Exists && FileSystem.GetLastWriteTimeUtc(file) > date;
+            return file.Exists && FileSystem.GetLastWriteTimeUtc(file) > item.DateLastSaved;
         }
 
         public string Name

+ 21 - 4
MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs

@@ -41,19 +41,36 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 }
             }
 
-            const string vn = " -vn";
-
             var threads = GetNumberOfThreads(state, false);
 
             var inputModifier = GetInputModifier(state);
 
-            return string.Format("{0} {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"",
+            var albumCoverInput = string.Empty;
+            var mapArgs = string.Empty;
+            var metadata = string.Empty;
+            var vn = string.Empty;
+
+            if (!string.IsNullOrWhiteSpace(state.AlbumCoverPath))
+            {
+                albumCoverInput = " -i \"" + state.AlbumCoverPath + "\"";
+                mapArgs = " -map 0:a -map 1:v -c:v copy";
+                metadata = " -metadata:s:v title=\"Album cover\" -metadata:s:v comment=\"Cover(Front)\"";
+            }
+            else
+            {
+                vn = " -vn";
+            }
+
+            return string.Format("{0} {1}{6}{7} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1{8} -y \"{5}\"",
                 inputModifier,
                 GetInputArgument(state),
                 threads,
                 vn,
                 string.Join(" ", audioTranscodeParams.ToArray()),
-                state.OutputFilePath).Trim();
+                state.OutputFilePath,
+                albumCoverInput,
+                mapArgs,
+                metadata).Trim();
         }
 
         protected override string GetOutputFileExtension(EncodingJob state)

+ 9 - 3
MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs

@@ -366,9 +366,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
         /// <returns>System.String.</returns>
         protected string GetVideoDecoder(EncodingJob state)
         {
-            if (string.Equals(GetEncodingOptions().HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
+            if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
             {
-                if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec))
+                return null;    
+            }
+
+            if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec))
+            {
+                if (string.Equals(GetEncodingOptions().HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
                 {
                     switch (state.MediaSource.VideoStream.Codec.ToLower())
                     {
@@ -376,7 +381,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
                         case "h264":
                             if (MediaEncoder.SupportsDecoder("h264_qsv"))
                             {
-                                return "-c:v h264_qsv ";
+                                // Seeing stalls and failures with decoding. Not worth it compared to encoding.
+                                //return "-c:v h264_qsv ";
                             }
                             break;
                         case "mpeg2video":

+ 1 - 0
MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs

@@ -64,6 +64,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
         public long? InputFileSize { get; set; }
         public string OutputAudioSync = "1";
         public string OutputVideoSync = "vfr";
+        public string AlbumCoverPath { get; set; }
 
         public string GetMimeType(string outputPath)
         {

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

@@ -60,6 +60,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
 
+            var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ??
+                               item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null);
+
+            if (primaryImage != null)
+            {
+                state.AlbumCoverPath = primaryImage.Path;
+            }
+
             var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false);
 
             var mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
@@ -575,6 +583,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 return false;
             }
 
+            if (string.Equals("h264", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
+            {
+                if (videoStream.IsAVC.HasValue && !videoStream.IsAVC.Value)
+                {
+                    return false;
+                }
+            }
+
             // If client is requesting a specific video profile, it must match the source
             if (!string.IsNullOrEmpty(request.Profile))
             {

+ 24 - 2
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -96,15 +96,23 @@ namespace MediaBrowser.MediaEncoding.Encoder
             FFMpegPath = ffMpegPath;
         }
 
+        private List<string> _encoders = new List<string>();
         public void SetAvailableEncoders(List<string> list)
         {
-
+            _encoders = list.ToList();
+            //_logger.Info("Supported encoders: {0}", string.Join(",", list.ToArray()));
         }
 
         private List<string> _decoders = new List<string>();
         public void SetAvailableDecoders(List<string> list)
         {
             _decoders = list.ToList();
+            //_logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray()));
+        }
+
+        public bool SupportsEncoder(string decoder)
+        {
+            return _encoders.Contains(decoder, StringComparer.OrdinalIgnoreCase);
         }
 
         public bool SupportsDecoder(string decoder)
@@ -112,6 +120,20 @@ namespace MediaBrowser.MediaEncoding.Encoder
             return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase);
         }
 
+        public bool CanEncodeToAudioCodec(string codec)
+        {
+            if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase))
+            {
+                codec = "libopus";
+            }
+            else if (string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
+            {
+                codec = "libmp3lame";
+            }
+
+            return SupportsEncoder(codec);
+        }
+
         /// <summary>
         /// Gets the encoder path.
         /// </summary>
@@ -296,7 +318,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                                           formats.Contains("ts", StringComparer.OrdinalIgnoreCase) ||
                                           formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) ||
                                           formats.Contains("wtv", StringComparer.OrdinalIgnoreCase);
-            
+
             // If it's mpeg based, assume true
             if ((videoStream.Codec ?? string.Empty).IndexOf("mpeg", StringComparison.OrdinalIgnoreCase) != -1)
             {

+ 0 - 1
MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs

@@ -75,7 +75,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
             {
                 if (state.VideoStream != null && IsH264(state.VideoStream) && string.Equals(state.Options.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
                 {
-                    Logger.Debug("Enabling h264_mp4toannexb due to nal_length_size of {0}", state.VideoStream.NalLengthSize);
                     args += " -bsf:v h264_mp4toannexb";
                 }
 

+ 11 - 0
MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs

@@ -411,6 +411,17 @@ namespace MediaBrowser.MediaEncoding.Probing
                 NalLengthSize = streamInfo.nal_length_size
             };
 
+            if (string.Equals(streamInfo.is_avc, "true", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(streamInfo.is_avc, "1", StringComparison.OrdinalIgnoreCase))
+            {
+                stream.IsAVC = true;
+            }
+            else if (string.Equals(streamInfo.is_avc, "false", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(streamInfo.is_avc, "0", StringComparison.OrdinalIgnoreCase))
+            {
+                stream.IsAVC = false;
+            }
+
             // Filter out junk
             if (!string.IsNullOrWhiteSpace(streamInfo.codec_tag_string) && streamInfo.codec_tag_string.IndexOf("[0]", StringComparison.OrdinalIgnoreCase) == -1)
             {

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

@@ -201,6 +201,7 @@ namespace MediaBrowser.Model.Configuration
         public string[] Migrations { get; set; }
 
         public int MigrationVersion { get; set; }
+        public int SchemaVersion { get; set; }
 
         public bool DownloadImagesInAdvance { get; set; }
 

+ 13 - 0
MediaBrowser.Model/Dlna/ILocalPlayer.cs

@@ -23,4 +23,17 @@ namespace MediaBrowser.Model.Dlna
         /// <returns><c>true</c> if this instance [can access URL] the specified URL; otherwise, <c>false</c>.</returns>
         bool CanAccessUrl(string url, bool requiresCustomRequestHeaders);
     }
+
+    public interface ITranscoderSupport
+    {
+        bool CanEncodeToAudioCodec(string codec);
+    }
+
+    public class FullTranscoderSupport : ITranscoderSupport
+    {
+        public bool CanEncodeToAudioCodec(string codec)
+        {
+            return true;
+        }
+    }
 }

+ 32 - 4
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -13,15 +13,27 @@ namespace MediaBrowser.Model.Dlna
     {
         private readonly ILocalPlayer _localPlayer;
         private readonly ILogger _logger;
+        private readonly ITranscoderSupport _transcoderSupport;
 
-        public StreamBuilder(ILocalPlayer localPlayer, ILogger logger)
+        public StreamBuilder(ILocalPlayer localPlayer, ITranscoderSupport transcoderSupport, ILogger logger)
         {
+            _transcoderSupport = transcoderSupport;
             _localPlayer = localPlayer;
             _logger = logger;
         }
 
+        public StreamBuilder(ITranscoderSupport transcoderSupport, ILogger logger)
+            : this(new NullLocalPlayer(), transcoderSupport, logger)
+        {
+        }
+
+        public StreamBuilder(ILocalPlayer localPlayer, ILogger logger)
+            : this(localPlayer, new FullTranscoderSupport(), logger)
+        {
+        }
+
         public StreamBuilder(ILogger logger)
-            : this(new NullLocalPlayer(), logger)
+            : this(new NullLocalPlayer(), new FullTranscoderSupport(), logger)
         {
         }
 
@@ -185,8 +197,11 @@ namespace MediaBrowser.Model.Dlna
             {
                 if (i.Type == playlistItem.MediaType && i.Context == options.Context)
                 {
-                    transcodingProfile = i;
-                    break;
+                    if (_transcoderSupport.CanEncodeToAudioCodec(i.AudioCodec ?? i.Container))
+                    {
+                        transcodingProfile = i;
+                        break;
+                    }
                 }
             }
 
@@ -1038,6 +1053,18 @@ namespace MediaBrowser.Model.Dlna
                 }
             }
 
+            // Check audio codec
+            List<string> audioCodecs = profile.GetAudioCodecs();
+            if (audioCodecs.Count > 0)
+            {
+                // Check audio codecs
+                string audioCodec = audioStream == null ? null : audioStream.Codec;
+                if (string.IsNullOrEmpty(audioCodec) || !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec))
+                {
+                    return false;
+                }
+            }
+
             return true;
         }
 
@@ -1073,6 +1100,7 @@ namespace MediaBrowser.Model.Dlna
                 }
             }
 
+            // Check audio codec
             List<string> audioCodecs = profile.GetAudioCodecs();
             if (audioCodecs.Count > 0)
             {

+ 1 - 1
MediaBrowser.Model/Dlna/TranscodingProfile.cs

@@ -19,7 +19,7 @@ namespace MediaBrowser.Model.Dlna
 
         [XmlAttribute("protocol")]
         public string Protocol { get; set; }
-        
+
         [XmlAttribute("estimateContentLength")]
         public bool EstimateContentLength { get; set; }
 

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

@@ -26,6 +26,8 @@ namespace MediaBrowser.Model.Dto
         /// <value>The name.</value>
         public string Name { get; set; }
 
+        public string OriginalTitle { get; set; }
+
         /// <summary>
         /// Gets or sets the server identifier.
         /// </summary>

+ 2 - 0
MediaBrowser.Model/Entities/MediaStream.cs

@@ -42,6 +42,8 @@ namespace MediaBrowser.Model.Entities
         /// <value><c>true</c> if this instance is interlaced; otherwise, <c>false</c>.</value>
         public bool IsInterlaced { get; set; }
 
+        public bool? IsAVC { get; set; }
+
         /// <summary>
         /// Gets or sets the channel layout.
         /// </summary>

+ 1 - 0
MediaBrowser.Model/LiveTv/LiveTvOptions.cs

@@ -9,6 +9,7 @@ namespace MediaBrowser.Model.LiveTv
         public string RecordingPath { get; set; }
         public bool EnableAutoOrganize { get; set; }
         public bool EnableRecordingEncoding { get; set; }
+        public bool EnableOriginalAudioWithEncodedRecordings { get; set; }
 
         public List<TunerHostInfo> TunerHosts { get; set; }
         public List<ListingsProviderInfo> ListingProviders { get; set; }

+ 2 - 0
MediaBrowser.Model/Querying/ItemFields.cs

@@ -130,6 +130,8 @@
         /// </summary>
         Metascore,
 
+        OriginalTitle,
+
         /// <summary>
         /// The item overview
         /// </summary>

+ 2 - 0
MediaBrowser.Model/Sync/SyncJobItem.cs

@@ -102,6 +102,8 @@ namespace MediaBrowser.Model.Sync
         /// <value>The index of the job item.</value>
         public int JobItemIndex { get; set; }
 
+        public long ItemDateModifiedTicks { get; set; }
+
         public SyncJobItem()
         {
             AdditionalFiles = new List<ItemFileInfo>();

+ 1 - 1
MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs

@@ -61,7 +61,7 @@ namespace MediaBrowser.Providers.BoxSets
                 {
                     var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
 
-                    var tmdbImageUrl = tmdbSettings.images.base_url + "original";
+                    var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
 
                     return GetImages(mainResult, language, tmdbImageUrl);
                 }

+ 2 - 2
MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.Providers.BoxSets
 {
     public class MovieDbBoxSetProvider : IRemoteMetadataProvider<BoxSet, BoxSetInfo>
     {
-        private const string GetCollectionInfo3 = @"http://api.themoviedb.org/3/collection/{0}?api_key={1}&append_to_response=images";
+        private const string GetCollectionInfo3 = @"https://api.themoviedb.org/3/collection/{0}?api_key={1}&append_to_response=images";
 
         internal static MovieDbBoxSetProvider Current;
 
@@ -64,7 +64,7 @@ namespace MediaBrowser.Providers.BoxSets
 
                 var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
 
-                var tmdbImageUrl = tmdbSettings.images.base_url + "original";
+                var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
 
                 var result = new RemoteSearchResult
                 {

+ 9 - 14
MediaBrowser.Providers/Manager/MetadataService.cs

@@ -295,22 +295,17 @@ namespace MediaBrowser.Providers.Manager
                 return true;
             }
 
-            if (item is BoxSet || item is IItemByName || item is Playlist)
-            {
-                return true;
-            }
-
-            if (item.SourceType != SourceType.Library)
+            if (!(item is Audio) && !(item is Video))
             {
                 return true;
             }
 
-            if (item is ICollectionFolder)
+            if (item is IItemByName)
             {
                 return true;
             }
 
-            if (!(item is Audio) && !(item is Video))
+            if (item.SourceType != SourceType.Library)
             {
                 return true;
             }
@@ -435,18 +430,18 @@ namespace MediaBrowser.Providers.Manager
                 var providersWithChanges = providers
                     .Where(i =>
                     {
-                        var hasChangeMonitor = i as IHasChangeMonitor;
-                        if (hasChangeMonitor != null)
-                        {
-                            return HasChanged(item, hasChangeMonitor, currentItem.DateLastSaved, options.DirectoryService);
-                        }
-
                         var hasFileChangeMonitor = i as IHasItemChangeMonitor;
                         if (hasFileChangeMonitor != null)
                         {
                             return HasChanged(item, hasFileChangeMonitor, options.DirectoryService);
                         }
 
+                        var hasChangeMonitor = i as IHasChangeMonitor;
+                        if (hasChangeMonitor != null)
+                        {
+                            return HasChanged(item, hasChangeMonitor, currentItem.DateLastSaved, options.DirectoryService);
+                        }
+
                         return false;
                     })
                     .ToList();

+ 9 - 0
MediaBrowser.Providers/Manager/ProviderUtils.cs

@@ -40,6 +40,15 @@ namespace MediaBrowser.Providers.Manager
                 }
             }
 
+            if (replaceData || string.IsNullOrEmpty(target.OriginalTitle))
+            {
+                // Safeguard against incoming data having an emtpy name
+                if (!string.IsNullOrWhiteSpace(source.OriginalTitle))
+                {
+                    target.OriginalTitle = source.OriginalTitle;
+                }
+            }
+
             if (replaceData || !target.CommunityRating.HasValue)
             {
                 target.CommunityRating = source.CommunityRating;

+ 5 - 1
MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs

@@ -444,7 +444,11 @@ namespace MediaBrowser.Providers.MediaInfo
             {
                 if (string.IsNullOrWhiteSpace(video.Name) || string.Equals(video.Name, Path.GetFileNameWithoutExtension(video.Path), StringComparison.OrdinalIgnoreCase))
                 {
-                    video.Name = data.Name;
+                    // Don't use the embedded name for extras because it will often be the same name as the movie
+                    if (!video.ExtraType.HasValue && !video.IsOwnedItem)
+                    {
+                        video.Name = data.Name;
+                    }
                 }
             }
 

+ 1 - 1
MediaBrowser.Providers/Movies/FanArtMovieUpdatesPostScanTask.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.Movies
 {
     class FanartMovieUpdatesPostScanTask : ILibraryPostScanTask
     {
-        private const string UpdatesUrl = "http://webservice.fanart.tv/v3/movies/latest?api_key={0}&date={1}";
+        private const string UpdatesUrl = "https://webservice.fanart.tv/v3/movies/latest?api_key={0}&date={1}";
 
         /// <summary>
         /// The _HTTP client

+ 7 - 5
MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs

@@ -16,6 +16,7 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
+using System.Text.RegularExpressions;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
@@ -23,7 +24,7 @@ using MediaBrowser.Providers.TV;
 
 namespace MediaBrowser.Providers.Movies
 {
-    public class FanartMovieImageProvider : IRemoteImageProvider, IHasChangeMonitor, IHasOrder
+    public class FanartMovieImageProvider : IRemoteImageProvider, IHasItemChangeMonitor, IHasOrder
     {
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
         private readonly IServerConfigurationManager _config;
@@ -31,7 +32,7 @@ namespace MediaBrowser.Providers.Movies
         private readonly IFileSystem _fileSystem;
         private readonly IJsonSerializer _json;
 
-        private const string FanArtBaseUrl = "http://webservice.fanart.tv/v3/movies/{1}?api_key={0}";
+        private const string FanArtBaseUrl = "https://webservice.fanart.tv/v3/movies/{1}?api_key={0}";
         // &client_key=52c813aa7b8c8b3bb87f4797532a2f8c
 
         internal static FanartMovieImageProvider Current;
@@ -185,6 +186,7 @@ namespace MediaBrowser.Providers.Movies
             PopulateImages(list, obj.moviebackground, ImageType.Backdrop, 1920, 1080);
         }
 
+        private Regex _regex_http = new Regex("^http://");
         private void PopulateImages(List<RemoteImageInfo> list, List<Image> images, ImageType type, int width, int height)
         {
             if (images == null)
@@ -208,7 +210,7 @@ namespace MediaBrowser.Providers.Movies
                         Width = width,
                         Height = height,
                         ProviderName = Name,
-                        Url = url,
+                        Url = _regex_http.Replace(url, "https://", 1),
                         Language = i.lang
                     };
 
@@ -239,7 +241,7 @@ namespace MediaBrowser.Providers.Movies
             });
         }
 
-        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
+        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
         {
             var options = FanartSeriesProvider.Current.GetFanartOptions();
             if (!options.EnableAutomaticUpdates)
@@ -260,7 +262,7 @@ namespace MediaBrowser.Providers.Movies
 
                 var fileInfo = _fileSystem.GetFileInfo(path);
 
-                return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date;
+                return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > item.DateLastRefreshed;
             }
 
             return false;

+ 2 - 2
MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs

@@ -249,7 +249,7 @@ namespace MediaBrowser.Providers.Movies
             }
 
             resultItem.ResetPeople();
-            var tmdbImageUrl = settings.images.base_url + "original";
+            var tmdbImageUrl = settings.images.secure_base_url + "original";
 
             //Actors, Directors, Writers - all in People
             //actors come from cast
@@ -329,7 +329,7 @@ namespace MediaBrowser.Providers.Movies
                 {
                     hasTrailers.RemoteTrailers = movieData.trailers.youtube.Select(i => new MediaUrl
                     {
-                        Url = string.Format("http://www.youtube.com/watch?v={0}", i.source),
+                        Url = string.Format("https://www.youtube.com/watch?v={0}", i.source),
                         Name = i.name,
                         VideoSize = string.Equals("hd", i.size, StringComparison.OrdinalIgnoreCase) ? VideoSize.HighDefinition : VideoSize.StandardDefinition
 

+ 4 - 4
MediaBrowser.Providers/Movies/MovieDbImageProvider.cs

@@ -17,7 +17,7 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Providers.Movies
 {
-    class MovieDbImageProvider : IRemoteImageProvider, IHasOrder, IHasChangeMonitor
+    class MovieDbImageProvider : IRemoteImageProvider, IHasOrder, IHasItemChangeMonitor
     {
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IHttpClient _httpClient;
@@ -72,7 +72,7 @@ namespace MediaBrowser.Providers.Movies
 
             var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
 
-            var tmdbImageUrl = tmdbSettings.images.base_url + "original";
+            var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
 
             var supportedImages = GetSupportedImages(item).ToList();
 
@@ -222,9 +222,9 @@ namespace MediaBrowser.Providers.Movies
             });
         }
 
-        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
+        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
         {
-            return MovieDbProvider.Current.HasChanged(item, date);
+            return MovieDbProvider.Current.HasChanged(item);
         }
     }
 }

+ 6 - 6
MediaBrowser.Providers/Movies/MovieDbProvider.cs

@@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.Movies
 
                 var tmdbSettings = await GetTmdbSettings(cancellationToken).ConfigureAwait(false);
 
-                var tmdbImageUrl = tmdbSettings.images.base_url + "original";
+                var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
 
                 var remoteResult = new RemoteSearchResult
                 {
@@ -172,8 +172,8 @@ namespace MediaBrowser.Providers.Movies
             }
         }
 
-        private const string TmdbConfigUrl = "http://api.themoviedb.org/3/configuration?api_key={0}";
-        private const string GetMovieInfo3 = @"http://api.themoviedb.org/3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers";
+        private const string TmdbConfigUrl = "https://api.themoviedb.org/3/configuration?api_key={0}";
+        private const string GetMovieInfo3 = @"https://api.themoviedb.org/3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers";
 
         internal static string ApiKey = "f6bd687ffa63cd282b6ff2c6877f2669";
         internal static string AcceptHeader = "application/json,image/*";
@@ -281,7 +281,7 @@ namespace MediaBrowser.Providers.Movies
         public static string NormalizeLanguage(string language)
         {
             // They require this to be uppercase
-            // http://emby.media/community/index.php?/topic/32454-fr-follow-tmdbs-new-language-api-update/?p=311148
+            // https://emby.media/community/index.php?/topic/32454-fr-follow-tmdbs-new-language-api-update/?p=311148
             var parts = language.Split('-');
 
             if (parts.Length == 2)
@@ -414,7 +414,7 @@ namespace MediaBrowser.Providers.Movies
             return _configurationManager.GetConfiguration<TheMovieDbOptions>("themoviedb");
         }
 
-        public bool HasChanged(IHasMetadata item, DateTime date)
+        public bool HasChanged(IHasMetadata item)
         {
             if (!GetTheMovieDbOptions().EnableAutomaticUpdates)
             {
@@ -430,7 +430,7 @@ namespace MediaBrowser.Providers.Movies
 
                 var fileInfo = _fileSystem.GetFileInfo(dataFilePath);
 
-                return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date;
+                return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > item.DateLastRefreshed;
             }
 
             return false;

+ 2 - 2
MediaBrowser.Providers/Movies/MovieDbSearch.cs

@@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.Movies
     public class MovieDbSearch
     {
         private static readonly CultureInfo EnUs = new CultureInfo("en-US");
-        private const string Search3 = @"http://api.themoviedb.org/3/search/{3}?api_key={1}&query={0}&language={2}";
+        private const string Search3 = @"https://api.themoviedb.org/3/search/{3}?api_key={1}&query={0}&language={2}";
 
         internal static string ApiKey = "f6bd687ffa63cd282b6ff2c6877f2669";
         internal static string AcceptHeader = "application/json,image/*";
@@ -56,7 +56,7 @@ namespace MediaBrowser.Providers.Movies
 
             var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
 
-            var tmdbImageUrl = tmdbSettings.images.base_url + "original";
+            var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
 
             if (!string.IsNullOrWhiteSpace(name))
             {

+ 2 - 2
MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs

@@ -33,9 +33,9 @@ namespace MediaBrowser.Providers.Movies
             get { return MovieDbProvider.Current.Name; }
         }
 
-        public bool HasChanged(IHasMetadata item, DateTime date)
+        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
         {
-            return MovieDbProvider.Current.HasChanged(item, date);
+            return MovieDbProvider.Current.HasChanged(item);
         }
 
         public int Order

+ 5 - 5
MediaBrowser.Providers/Movies/MovieExternalIds.cs

@@ -21,7 +21,7 @@ namespace MediaBrowser.Providers.Movies
 
         public string UrlFormatString
         {
-            get { return "http://www.themoviedb.org/movie/{0}"; }
+            get { return "https://www.themoviedb.org/movie/{0}"; }
         }
 
         public bool Supports(IHasProviderIds item)
@@ -51,7 +51,7 @@ namespace MediaBrowser.Providers.Movies
 
         public string UrlFormatString
         {
-            get { return "http://www.themoviedb.org/tv/{0}"; }
+            get { return "https://www.themoviedb.org/tv/{0}"; }
         }
 
         public bool Supports(IHasProviderIds item)
@@ -74,7 +74,7 @@ namespace MediaBrowser.Providers.Movies
 
         public string UrlFormatString
         {
-            get { return "http://www.themoviedb.org/collection/{0}"; }
+            get { return "https://www.themoviedb.org/collection/{0}"; }
         }
 
         public bool Supports(IHasProviderIds item)
@@ -97,7 +97,7 @@ namespace MediaBrowser.Providers.Movies
 
         public string UrlFormatString
         {
-            get { return "http://www.themoviedb.org/person/{0}"; }
+            get { return "https://www.themoviedb.org/person/{0}"; }
         }
 
         public bool Supports(IHasProviderIds item)
@@ -120,7 +120,7 @@ namespace MediaBrowser.Providers.Movies
 
         public string UrlFormatString
         {
-            get { return "http://www.themoviedb.org/collection/{0}"; }
+            get { return "https://www.themoviedb.org/collection/{0}"; }
         }
 
         public bool Supports(IHasProviderIds item)

+ 1 - 1
MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs

@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Movies
         /// <summary>
         /// The updates URL
         /// </summary>
-        private const string UpdatesUrl = "http://api.themoviedb.org/3/movie/changes?start_date={0}&api_key={1}&page={2}";
+        private const string UpdatesUrl = "https://api.themoviedb.org/3/movie/changes?start_date={0}&api_key={1}&page={2}";
 
         /// <summary>
         /// The _HTTP client

+ 1 - 1
MediaBrowser.Providers/Movies/TmdbSettings.cs

@@ -5,7 +5,7 @@ namespace MediaBrowser.Providers.Movies
     internal class TmdbImageSettings
     {
         public List<string> backdrop_sizes { get; set; }
-        public string base_url { get; set; }
+        public string secure_base_url { get; set; }
         public List<string> poster_sizes { get; set; }
         public List<string> profile_sizes { get; set; }
     }

+ 3 - 3
MediaBrowser.Providers/Music/FanArtAlbumProvider.cs

@@ -19,7 +19,7 @@ using MediaBrowser.Model.Serialization;
 
 namespace MediaBrowser.Providers.Music
 {
-    public class FanartAlbumProvider : IRemoteImageProvider, IHasChangeMonitor, IHasOrder
+    public class FanartAlbumProvider : IRemoteImageProvider, IHasItemChangeMonitor, IHasOrder
     {
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
         private readonly IServerConfigurationManager _config;
@@ -213,7 +213,7 @@ namespace MediaBrowser.Providers.Music
             });
         }
 
-        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
+        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
         {
             var options = FanartSeriesProvider.Current.GetFanartOptions();
             if (!options.EnableAutomaticUpdates)
@@ -235,7 +235,7 @@ namespace MediaBrowser.Providers.Music
 
                     var fileInfo = _fileSystem.GetFileInfo(artistJsonPath);
 
-                    return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date;
+                    return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > item.DateLastRefreshed;
                 }
             }
 

+ 4 - 4
MediaBrowser.Providers/Music/FanArtArtistProvider.cs

@@ -22,11 +22,11 @@ using MediaBrowser.Model.Serialization;
 
 namespace MediaBrowser.Providers.Music
 {
-    public class FanartArtistProvider : IRemoteImageProvider, IHasChangeMonitor, IHasOrder
+    public class FanartArtistProvider : IRemoteImageProvider, IHasItemChangeMonitor, IHasOrder
     {
         internal readonly SemaphoreSlim FanArtResourcePool = new SemaphoreSlim(3, 3);
         internal const string ApiKey = "5c6b04c68e904cfed1e6cbc9a9e683d4";
-        private const string FanArtBaseUrl = "http://webservice.fanart.tv/v3.1/music/{1}?api_key={0}";
+        private const string FanArtBaseUrl = "https://webservice.fanart.tv/v3.1/music/{1}?api_key={0}";
 
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
         private readonly IServerConfigurationManager _config;
@@ -207,7 +207,7 @@ namespace MediaBrowser.Providers.Music
             });
         }
 
-        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
+        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
         {
             var options = FanartSeriesProvider.Current.GetFanartOptions();
             if (!options.EnableAutomaticUpdates)
@@ -224,7 +224,7 @@ namespace MediaBrowser.Providers.Music
 
                 var fileInfo = _fileSystem.GetFileInfo(artistJsonPath);
 
-                return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date;
+                return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > item.DateLastRefreshed;
             }
 
             return false;

+ 1 - 1
MediaBrowser.Providers/Music/FanArtUpdatesPostScanTask.cs

@@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.Music
 {
     class FanartUpdatesPostScanTask : ILibraryPostScanTask
     {
-        private const string UpdatesUrl = "http://api.fanart.tv/webservice/newmusic/{0}/{1}/";
+        private const string UpdatesUrl = "https://api.fanart.tv/webservice/newmusic/{0}/{1}/";
 
         /// <summary>
         /// The _HTTP client

+ 2 - 2
MediaBrowser.Providers/Music/MovieDbMusicVideoProvider.cs

@@ -27,9 +27,9 @@ namespace MediaBrowser.Providers.Music
             get { return MovieDbProvider.Current.Name; }
         }
 
-        public bool HasChanged(IHasMetadata item, DateTime date)
+        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
         {
-            return MovieDbProvider.Current.HasChanged(item, date);
+            return MovieDbProvider.Current.HasChanged(item);
         }
 
         public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)

+ 9 - 9
MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs

@@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.Music
 
             if (!string.IsNullOrEmpty(releaseId))
             {
-                url = string.Format("http://www.musicbrainz.org/ws/2/release/?query=reid:{0}", releaseId);
+                url = string.Format("https://www.musicbrainz.org/ws/2/release/?query=reid:{0}", releaseId);
             }
             else
             {
@@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.Music
 
                 if (!string.IsNullOrWhiteSpace(artistMusicBrainzId))
                 {
-                    url = string.Format("http://www.musicbrainz.org/ws/2/release/?query=\"{0}\" AND arid:{1}",
+                    url = string.Format("https://www.musicbrainz.org/ws/2/release/?query=\"{0}\" AND arid:{1}",
                         WebUtility.UrlEncode(searchInfo.Name),
                         artistMusicBrainzId);
                 }
@@ -58,7 +58,7 @@ namespace MediaBrowser.Providers.Music
                 {
                     isNameSearch = true;
 
-                    url = string.Format("http://www.musicbrainz.org/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"",
+                    url = string.Format("https://www.musicbrainz.org/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"",
                        WebUtility.UrlEncode(searchInfo.Name),
                        WebUtility.UrlEncode(searchInfo.GetAlbumArtist()));
                 }
@@ -77,7 +77,7 @@ namespace MediaBrowser.Providers.Music
         private IEnumerable<RemoteSearchResult> GetResultsFromResponse(XmlDocument doc)
         {
             var ns = new XmlNamespaceManager(doc.NameTable);
-            ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#");
+            ns.AddNamespace("mb", "https://musicbrainz.org/ns/mmd-2.0#");
 
             var list = new List<RemoteSearchResult>();
 
@@ -197,7 +197,7 @@ namespace MediaBrowser.Providers.Music
 
         private async Task<ReleaseResult> GetReleaseResult(string albumName, string artistId, CancellationToken cancellationToken)
         {
-            var url = string.Format("http://www.musicbrainz.org/ws/2/release/?query=\"{0}\" AND arid:{1}",
+            var url = string.Format("https://www.musicbrainz.org/ws/2/release/?query=\"{0}\" AND arid:{1}",
                 WebUtility.UrlEncode(albumName),
                 artistId);
 
@@ -208,7 +208,7 @@ namespace MediaBrowser.Providers.Music
 
         private async Task<ReleaseResult> GetReleaseResultByArtistName(string albumName, string artistName, CancellationToken cancellationToken)
         {
-            var url = string.Format("http://www.musicbrainz.org/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"",
+            var url = string.Format("https://www.musicbrainz.org/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"",
                 WebUtility.UrlEncode(albumName),
                 WebUtility.UrlEncode(artistName));
 
@@ -220,7 +220,7 @@ namespace MediaBrowser.Providers.Music
         private ReleaseResult GetReleaseResult(XmlDocument doc)
         {
             var ns = new XmlNamespaceManager(doc.NameTable);
-            ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#");
+            ns.AddNamespace("mb", "https://musicbrainz.org/ns/mmd-2.0#");
 
             var result = new ReleaseResult
             {
@@ -258,12 +258,12 @@ namespace MediaBrowser.Providers.Music
         /// <returns>Task{System.String}.</returns>
         private async Task<string> GetReleaseGroupId(string releaseEntryId, CancellationToken cancellationToken)
         {
-            var url = string.Format("http://www.musicbrainz.org/ws/2/release-group/?query=reid:{0}", releaseEntryId);
+            var url = string.Format("https://www.musicbrainz.org/ws/2/release-group/?query=reid:{0}", releaseEntryId);
 
             var doc = await GetMusicBrainzResponse(url, false, cancellationToken).ConfigureAwait(false);
 
             var ns = new XmlNamespaceManager(doc.NameTable);
-            ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#");
+            ns.AddNamespace("mb", "https://musicbrainz.org/ns/mmd-2.0#");
             var node = doc.SelectSingleNode("//mb:release-group-list/mb:release-group/@id", ns);
 
             return node != null ? node.Value : null;

+ 4 - 4
MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.Providers.Music
 
             if (!string.IsNullOrWhiteSpace(musicBrainzId))
             {
-                var url = string.Format("http://www.musicbrainz.org/ws/2/artist/?query=arid:{0}", musicBrainzId);
+                var url = string.Format("https://www.musicbrainz.org/ws/2/artist/?query=arid:{0}", musicBrainzId);
 
                 var doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, false, cancellationToken)
                             .ConfigureAwait(false);
@@ -35,7 +35,7 @@ namespace MediaBrowser.Providers.Music
                 // They seem to throw bad request failures on any term with a slash
                 var nameToSearch = searchInfo.Name.Replace('/', ' ');
 
-                var url = String.Format("http://www.musicbrainz.org/ws/2/artist/?query=artist:\"{0}\"", UrlEncode(nameToSearch));
+                var url = String.Format("https://www.musicbrainz.org/ws/2/artist/?query=artist:\"{0}\"", UrlEncode(nameToSearch));
 
                 var doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false);
 
@@ -49,7 +49,7 @@ namespace MediaBrowser.Providers.Music
                 if (HasDiacritics(searchInfo.Name))
                 {
                     // Try again using the search with accent characters url
-                    url = String.Format("http://www.musicbrainz.org/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
+                    url = String.Format("https://www.musicbrainz.org/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
 
                     doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false);
 
@@ -63,7 +63,7 @@ namespace MediaBrowser.Providers.Music
         private IEnumerable<RemoteSearchResult> GetResultsFromResponse(XmlDocument doc)
         {
             var ns = new XmlNamespaceManager(doc.NameTable);
-            ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#");
+            ns.AddNamespace("mb", "https://musicbrainz.org/ns/mmd-2.0#");
 
             var list = new List<RemoteSearchResult>();
 

+ 6 - 6
MediaBrowser.Providers/Music/MusicExternalIds.cs

@@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.Music
 
         public string UrlFormatString
         {
-            get { return "http://musicbrainz.org/release-group/{0}"; }
+            get { return "https://musicbrainz.org/release-group/{0}"; }
         }
 
         public bool Supports(IHasProviderIds item)
@@ -41,7 +41,7 @@ namespace MediaBrowser.Providers.Music
 
         public string UrlFormatString
         {
-            get { return "http://musicbrainz.org/artist/{0}"; }
+            get { return "https://musicbrainz.org/artist/{0}"; }
         }
 
         public bool Supports(IHasProviderIds item)
@@ -64,7 +64,7 @@ namespace MediaBrowser.Providers.Music
 
         public string UrlFormatString
         {
-            get { return "http://musicbrainz.org/release/{0}"; }
+            get { return "https://musicbrainz.org/release/{0}"; }
         }
 
         public bool Supports(IHasProviderIds item)
@@ -87,7 +87,7 @@ namespace MediaBrowser.Providers.Music
 
         public string UrlFormatString
         {
-            get { return "http://musicbrainz.org/artist/{0}"; }
+            get { return "https://musicbrainz.org/artist/{0}"; }
         }
 
         public bool Supports(IHasProviderIds item)
@@ -110,7 +110,7 @@ namespace MediaBrowser.Providers.Music
 
         public string UrlFormatString
         {
-            get { return "http://musicbrainz.org/artist/{0}"; }
+            get { return "https://musicbrainz.org/artist/{0}"; }
         }
 
         public bool Supports(IHasProviderIds item)
@@ -133,7 +133,7 @@ namespace MediaBrowser.Providers.Music
 
         public string UrlFormatString
         {
-            get { return "http://musicbrainz.org/track/{0}"; }
+            get { return "https://musicbrainz.org/track/{0}"; }
         }
 
         public bool Supports(IHasProviderIds item)

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác