diff options
127 files changed, 1246 insertions, 596 deletions
diff --git a/MediaBrowser.Api/BrandingService.cs b/MediaBrowser.Api/BrandingService.cs index c900e4d06..e991565ad 100644 --- a/MediaBrowser.Api/BrandingService.cs +++ b/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 { } diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index f37874774..5bb4ed5f0 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/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) { diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 901554973..800f246c4 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/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"; } } diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index cb344690c..49f50735f 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/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); - } } }
\ No newline at end of file diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index bc155ff45..f857a43e4 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/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); } diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 87b1c4248..8a14948d2 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/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; diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index ffe7c50c8..2d9cc40c0 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/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 { diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 3319fbaec..be3995aeb 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/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"; } diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index ed8a27faf..061afed6d 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/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 diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index 2017b40f4..8da1beb68 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/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" /> diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config index 64b337221..6d7d86182 100644 --- a/MediaBrowser.Common.Implementations/packages.config +++ b/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>
\ No newline at end of file diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 475e2ace8..9171c2a71 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/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> @@ -1540,11 +1542,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 +1555,15 @@ namespace MediaBrowser.Controller.Entities return null; } + [IgnoreDataMember] + public virtual bool EnableRememberingTrackSelections + { + get + { + return true; + } + } + /// <summary> /// Adds a studio to the item /// </summary> diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 429700327..5e0cf6e88 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/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] diff --git a/MediaBrowser.Controller/Entities/IHasUserData.cs b/MediaBrowser.Controller/Entities/IHasUserData.cs index 34a820853..faddc3778 100644 --- a/MediaBrowser.Controller/Entities/IHasUserData.cs +++ b/MediaBrowser.Controller/Entities/IHasUserData.cs @@ -20,5 +20,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; } } } diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 4d2ca9ffe..6004992cc 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/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; } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 9eeb89a1f..ad4ee436e 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/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() @@ -257,7 +255,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 +268,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; diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index c3e24090c..fe8bf3ed3 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/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; } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 2c7d3856b..d81e6f0ff 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -317,6 +317,11 @@ namespace MediaBrowser.Controller.Entities { return false; } + + if (newAsVideo.VideoType != VideoType) + { + return false; + } } return base.IsValidFromResolver(newItem); diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index c8b3d5131..5388b8668 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/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<UserView>.</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<PersonInfo>.</returns> List<PersonInfo> GetPeople(InternalPeopleQuery query); - + /// <summary> /// Gets the people items. /// </summary> /// <param name="query">The query.</param> /// <returns>List<Person>.</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<BaseItem>.</returns> QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query); - + /// <summary> /// Ignores the file. /// </summary> diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs index f8039b2cf..29421ebaf 100644 --- a/MediaBrowser.Controller/Library/TVUtils.cs +++ b/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. diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 498602ddf..1e7aa3de5 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -46,6 +46,8 @@ namespace MediaBrowser.Controller.LiveTv /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task<List<MediaSourceInfo>>.</returns> Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); + + string ApplyDuration(string streamPath, TimeSpan duration); } public interface IConfigurableTunerHost { diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 35b9a1959..2bb6cc182 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -45,6 +45,15 @@ namespace MediaBrowser.Controller.LiveTv set { } } + [IgnoreDataMember] + public override bool EnableRememberingTrackSelections + { + get + { + return false; + } + } + /// <summary> /// Gets or sets the number. /// </summary> diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index d2feb4116..7c3959f6e 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/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. diff --git a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs b/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs index 21289970e..093b37df3 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs +++ b/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); } diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index 01c7c33b6..34fb2a6df 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs +++ b/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) diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 89d00eb32..af833a85c 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/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, diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs index 37584f006..733bda9ae 100644 --- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs +++ b/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(); } diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index db5e0ee29..80bb756ef 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/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, diff --git a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs index 18daef331..bbb9bf6de 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs +++ b/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); diff --git a/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs b/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs index a2eac41ec..5e0baa1f6 100644 --- a/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs +++ b/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); + } } } diff --git a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs index 4fc1d210d..4b64295ea 100644 --- a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs +++ b/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 diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs index 968d703be..2d5225344 100644 --- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs +++ b/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) diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 263efbb62..0ea48fab6 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/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": diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs index b23bd16f3..490a51128 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs +++ b/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) { diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index 070aae3a7..1544a78b6 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/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)) { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 01fb31d0c..399fdead9 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/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) { diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs index b8efedf09..3ab55168d 100644 --- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs +++ b/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"; } diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 38528d845..c7c001cee 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/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) { diff --git a/MediaBrowser.Model/Dlna/ILocalPlayer.cs b/MediaBrowser.Model/Dlna/ILocalPlayer.cs index 55e11ec4b..9de360023 100644 --- a/MediaBrowser.Model/Dlna/ILocalPlayer.cs +++ b/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; + } + } } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 1e6b7c729..07a4b8995 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/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) { diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs index bf6bf092f..36c357926 100644 --- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs +++ b/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; } diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index 7928d0bf9..49ec9f8ef 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/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> diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index fa7a51291..25252956b 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/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> diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index 46f630fe0..fc2b155ec 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/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; } diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index 1540f178a..cea638a39 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -130,6 +130,8 @@ /// </summary> Metascore, + OriginalTitle, + /// <summary> /// The item overview /// </summary> diff --git a/MediaBrowser.Model/Sync/SyncJobItem.cs b/MediaBrowser.Model/Sync/SyncJobItem.cs index 77464be58..1c72ccd52 100644 --- a/MediaBrowser.Model/Sync/SyncJobItem.cs +++ b/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>(); diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs index ff3d5a5b2..b3e73740d 100644 --- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs +++ b/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); } diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs index 2cac9b063..ab05c959e 100644 --- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs +++ b/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 { diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index eeec4ea56..ae48c996a 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/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(); diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs index 278d8ed71..59a2da460 100644 --- a/MediaBrowser.Providers/Manager/ProviderUtils.cs +++ b/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; diff --git a/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPostScanTask.cs b/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPostScanTask.cs index d207e6e7c..5262f8e3b 100644 --- a/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPostScanTask.cs +++ b/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 diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs index a1dbc1967..775e6dfb9 100644 --- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs +++ b/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; diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs index 6c6f6f0eb..d13716cba 100644 --- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs +++ b/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 diff --git a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs index e091cddc6..5958c3a0a 100644 --- a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs +++ b/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); } } } diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index 51051e41d..c588a9a69 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/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; diff --git a/MediaBrowser.Providers/Movies/MovieDbSearch.cs b/MediaBrowser.Providers/Movies/MovieDbSearch.cs index e8eeab9c5..ceb41178e 100644 --- a/MediaBrowser.Providers/Movies/MovieDbSearch.cs +++ b/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)) { diff --git a/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs b/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs index 336968a84..5fb3ea369 100644 --- a/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs +++ b/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 diff --git a/MediaBrowser.Providers/Movies/MovieExternalIds.cs b/MediaBrowser.Providers/Movies/MovieExternalIds.cs index 02c330267..3bceb976e 100644 --- a/MediaBrowser.Providers/Movies/MovieExternalIds.cs +++ b/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) diff --git a/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs b/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs index 7fa7e0d15..f7f0fd6cc 100644 --- a/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs +++ b/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 diff --git a/MediaBrowser.Providers/Movies/TmdbSettings.cs b/MediaBrowser.Providers/Movies/TmdbSettings.cs index 59e8f7cef..12bb77afc 100644 --- a/MediaBrowser.Providers/Movies/TmdbSettings.cs +++ b/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; } } diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs index 444046208..5b3bd87db 100644 --- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs +++ b/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; } } diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs index b715803ea..37b51da5a 100644 --- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs +++ b/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; diff --git a/MediaBrowser.Providers/Music/FanArtUpdatesPostScanTask.cs b/MediaBrowser.Providers/Music/FanArtUpdatesPostScanTask.cs index 30507b891..3b829af9e 100644 --- a/MediaBrowser.Providers/Music/FanArtUpdatesPostScanTask.cs +++ b/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 diff --git a/MediaBrowser.Providers/Music/MovieDbMusicVideoProvider.cs b/MediaBrowser.Providers/Music/MovieDbMusicVideoProvider.cs index 55dcc99f5..d031b3d6b 100644 --- a/MediaBrowser.Providers/Music/MovieDbMusicVideoProvider.cs +++ b/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) diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs index e73a98b6f..e41982ef6 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs +++ b/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; diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs index c04f80549..ad900123e 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs +++ b/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>(); diff --git a/MediaBrowser.Providers/Music/MusicExternalIds.cs b/MediaBrowser.Providers/Music/MusicExternalIds.cs index bcafdc6f6..814488df1 100644 --- a/MediaBrowser.Providers/Music/MusicExternalIds.cs +++ b/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) diff --git a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs index ae563b287..a1e038374 100644 --- a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Providers.Omdb list.Add(new RemoteImageInfo { ProviderName = Name, - Url = string.Format("http://img.omdbapi.com/?i={0}&apikey=82e83907", imdbId) + Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=82e83907", imdbId) }); } diff --git a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs index 8acaf30d5..a0d60c166 100644 --- a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs @@ -66,7 +66,7 @@ namespace MediaBrowser.Providers.Omdb var imdbId = searchInfo.GetProviderId(MetadataProviders.Imdb); - var url = "http://www.omdbapi.com/?plot=full&r=json"; + var url = "https://www.omdbapi.com/?plot=full&r=json"; if (type == "episode" && episodeSearchInfo != null) { episodeSearchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out imdbId); diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index b68f93cf6..44e250350 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -37,7 +37,7 @@ namespace MediaBrowser.Providers.Omdb var imdbParam = imdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? imdbId : "tt" + imdbId; - var url = string.Format("http://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam); + var url = string.Format("https://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam); using (var stream = await _httpClient.Get(new HttpRequestOptions { diff --git a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs index 2ac9fdfa0..93eee69ae 100644 --- a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs @@ -67,7 +67,7 @@ namespace MediaBrowser.Providers.People 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(images, item.GetPreferredMetadataLanguage(), tmdbImageUrl); } diff --git a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs index 2b37d0462..bb17b83ec 100644 --- a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs +++ b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs @@ -59,7 +59,7 @@ namespace MediaBrowser.Providers.People 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.IsNullOrEmpty(tmdbId)) { @@ -97,7 +97,7 @@ namespace MediaBrowser.Providers.People var requestCount = _requestCount; - if (requestCount >= 30) + if (requestCount >= 40) { //_logger.Debug("Throttling Tmdb people"); @@ -109,7 +109,7 @@ namespace MediaBrowser.Providers.People } } - var url = string.Format(@"http://api.themoviedb.org/3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(searchInfo.Name), MovieDbProvider.ApiKey); + var url = string.Format(@"https://api.themoviedb.org/3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(searchInfo.Name), MovieDbProvider.ApiKey); using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions { @@ -234,7 +234,7 @@ namespace MediaBrowser.Providers.People return; } - var url = string.Format(@"http://api.themoviedb.org/3/person/{1}?api_key={0}&append_to_response=credits,images,external_ids", MovieDbProvider.ApiKey, id); + var url = string.Format(@"https://api.themoviedb.org/3/person/{1}?api_key={0}&append_to_response=credits,images,external_ids", MovieDbProvider.ApiKey, id); using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions { diff --git a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs index e683907c4..673663d7f 100644 --- a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs @@ -21,7 +21,7 @@ using CommonIO; namespace MediaBrowser.Providers.TV { - public class FanArtSeasonProvider : IRemoteImageProvider, IHasOrder, IHasChangeMonitor + public class FanArtSeasonProvider : IRemoteImageProvider, IHasOrder, IHasItemChangeMonitor { private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IServerConfigurationManager _config; @@ -225,7 +225,7 @@ namespace MediaBrowser.Providers.TV }); } - public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date) + public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) { var options = FanartSeriesProvider.Current.GetFanartOptions(); if (!options.EnableAutomaticUpdates) @@ -250,7 +250,7 @@ namespace MediaBrowser.Providers.TV var fileInfo = _fileSystem.GetFileInfo(imagesFilePath); - return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date; + return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > item.DateLastRefreshed; } return false; diff --git a/MediaBrowser.Providers/TV/FanArt/FanArtTvUpdatesPostScanTask.cs b/MediaBrowser.Providers/TV/FanArt/FanArtTvUpdatesPostScanTask.cs index 049ffd7d8..37e76531b 100644 --- a/MediaBrowser.Providers/TV/FanArt/FanArtTvUpdatesPostScanTask.cs +++ b/MediaBrowser.Providers/TV/FanArt/FanArtTvUpdatesPostScanTask.cs @@ -19,7 +19,7 @@ namespace MediaBrowser.Providers.TV { class FanArtTvUpdatesPostScanTask : ILibraryPostScanTask { - private const string UpdatesUrl = "http://webservice.fanart.tv/v3/tv/latest?api_key={0}&date={1}"; + private const string UpdatesUrl = "https://webservice.fanart.tv/v3/tv/latest?api_key={0}&date={1}"; /// <summary> /// The _HTTP client diff --git a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs index 517951cb8..3c831dbbc 100644 --- a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs @@ -23,7 +23,7 @@ using CommonIO; namespace MediaBrowser.Providers.TV { - public class FanartSeriesProvider : IRemoteImageProvider, IHasOrder, IHasChangeMonitor + public class FanartSeriesProvider : IRemoteImageProvider, IHasOrder, IHasItemChangeMonitor { private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IServerConfigurationManager _config; @@ -31,7 +31,7 @@ namespace MediaBrowser.Providers.TV private readonly IFileSystem _fileSystem; private readonly IJsonSerializer _json; - private const string FanArtBaseUrl = "http://webservice.fanart.tv/v3/tv/{1}?api_key={0}"; + private const string FanArtBaseUrl = "https://webservice.fanart.tv/v3/tv/{1}?api_key={0}"; // &client_key=52c813aa7b8c8b3bb87f4797532a2f8c internal static FanartSeriesProvider Current { get; private set; } @@ -341,7 +341,7 @@ namespace MediaBrowser.Providers.TV } } - public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date) + public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) { var options = GetFanartOptions(); if (!options.EnableAutomaticUpdates) @@ -358,7 +358,7 @@ namespace MediaBrowser.Providers.TV var fileInfo = _fileSystem.GetFileInfo(imagesFilePath); - return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date; + return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > item.DateLastRefreshed; } return false; diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index efdf8ce5d..e79ad2dfb 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -27,6 +27,8 @@ namespace MediaBrowser.Providers.TV private readonly IFileSystem _fileSystem; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private static readonly SemaphoreSlim _resourceLock = new SemaphoreSlim(1, 1); + public static bool IsRunning = false; public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem) { @@ -37,13 +39,16 @@ namespace MediaBrowser.Providers.TV _fileSystem = fileSystem; } - public async Task Run(IEnumerable<IGrouping<string, Series>> series, CancellationToken cancellationToken) + public async Task Run(List<IGrouping<string, Series>> series, bool addNewItems, CancellationToken cancellationToken) { + await _resourceLock.WaitAsync(cancellationToken).ConfigureAwait(false); + IsRunning = true; + foreach (var seriesGroup in series) { try { - await Run(seriesGroup, cancellationToken).ConfigureAwait(false); + await Run(seriesGroup, addNewItems, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -58,9 +63,12 @@ namespace MediaBrowser.Providers.TV _logger.ErrorException("Error in missing episode provider for series id {0}", ex, seriesGroup.Key); } } + + IsRunning = false; + _resourceLock.Release(); } - private async Task Run(IGrouping<string, Series> group, CancellationToken cancellationToken) + private async Task Run(IGrouping<string, Series> group, bool addNewItems, CancellationToken cancellationToken) { var tvdbId = group.Key; @@ -110,7 +118,7 @@ namespace MediaBrowser.Providers.TV var hasNewEpisodes = false; - if (_config.Configuration.EnableInternetProviders) + if (_config.Configuration.EnableInternetProviders && addNewItems) { var seriesConfig = _config.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, typeof(Series).Name, StringComparison.OrdinalIgnoreCase)); @@ -427,7 +435,7 @@ namespace MediaBrowser.Providers.TV await season.AddChild(episode, cancellationToken).ConfigureAwait(false); - await episode.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) + await episode.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) { }, cancellationToken).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs index 5428e6c92..0fd6fa56f 100644 --- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs +++ b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs @@ -11,6 +11,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Plugins; +using MediaBrowser.Model.Tasks; namespace MediaBrowser.Providers.TV { @@ -46,14 +50,17 @@ namespace MediaBrowser.Providers.TV private async Task RunInternal(IProgress<double> progress, CancellationToken cancellationToken) { - var seriesList = _libraryManager.RootFolder - .GetRecursiveChildren(i => i is Series) - .Cast<Series>() - .ToList(); + var seriesList = _libraryManager.GetItemList(new InternalItemsQuery() + { + IncludeItemTypes = new[] { typeof(Series).Name }, + Recursive = true + + }).Cast<Series>().ToList(); var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList(); - await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem).Run(seriesGroups, cancellationToken).ConfigureAwait(false); + await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem) + .Run(seriesGroups, true, cancellationToken).ConfigureAwait(false); var numComplete = 0; @@ -82,7 +89,7 @@ namespace MediaBrowser.Providers.TV } } - private IEnumerable<IGrouping<string, Series>> FindSeriesGroups(List<Series> seriesList) + internal static IEnumerable<IGrouping<string, Series>> FindSeriesGroups(List<Series> seriesList) { var links = seriesList.ToDictionary(s => s, s => seriesList.Where(c => c != s && ShareProviderId(s, c)).ToList()); @@ -102,7 +109,7 @@ namespace MediaBrowser.Providers.TV } } - private void FindAllLinked(Series series, HashSet<Series> visited, IDictionary<Series, List<Series>> linksMap, List<Series> results) + private static void FindAllLinked(Series series, HashSet<Series> visited, IDictionary<Series, List<Series>> linksMap, List<Series> results) { results.Add(series); visited.Add(series); @@ -118,7 +125,7 @@ namespace MediaBrowser.Providers.TV } } - private bool ShareProviderId(Series a, Series b) + private static bool ShareProviderId(Series a, Series b) { return a.ProviderIds.Any(id => { @@ -137,4 +144,108 @@ namespace MediaBrowser.Providers.TV } } + public class CleanMissingEpisodesEntryPoint : IServerEntryPoint + { + private readonly ILibraryManager _libraryManager; + private readonly IServerConfigurationManager _config; + private readonly ILogger _logger; + private readonly ILocalizationManager _localization; + private readonly IFileSystem _fileSystem; + private readonly object _libraryChangedSyncLock = new object(); + private const int LibraryUpdateDuration = 180000; + private readonly ITaskManager _taskManager; + + public CleanMissingEpisodesEntryPoint(ILibraryManager libraryManager, IServerConfigurationManager config, ILogger logger, ILocalizationManager localization, IFileSystem fileSystem, ITaskManager taskManager) + { + _libraryManager = libraryManager; + _config = config; + _logger = logger; + _localization = localization; + _fileSystem = fileSystem; + _taskManager = taskManager; + } + + private Timer LibraryUpdateTimer { get; set; } + + public void Run() + { + _libraryManager.ItemAdded += _libraryManager_ItemAdded; + } + + private void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e) + { + if (!FilterItem(e.Item)) + { + return; + } + + lock (_libraryChangedSyncLock) + { + if (LibraryUpdateTimer == null) + { + LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, Timeout.Infinite); + } + else + { + LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite); + } + } + } + + private async void LibraryUpdateTimerCallback(object state) + { + if (MissingEpisodeProvider.IsRunning) + { + return; + } + + if (_libraryManager.IsScanRunning) + { + return ; + } + + var seriesList = _libraryManager.GetItemList(new InternalItemsQuery() + { + IncludeItemTypes = new[] { typeof(Series).Name }, + Recursive = true + + }).Cast<Series>().ToList(); + + var seriesGroups = SeriesPostScanTask.FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList(); + + await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem) + .Run(seriesGroups, false, CancellationToken.None).ConfigureAwait(false); + } + + private bool FilterItem(BaseItem item) + { + return item is Episode && item.LocationType != LocationType.Virtual; + } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + Dispose(true); + } + + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + if (LibraryUpdateTimer != null) + { + LibraryUpdateTimer.Dispose(); + LibraryUpdateTimer = null; + } + + _libraryManager.ItemAdded -= _libraryManager_ItemAdded; + } + } + } } diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs index 9d1684948..719779674 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs @@ -62,7 +62,7 @@ namespace MediaBrowser.Providers.TV var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - var tmdbImageUrl = tmdbSettings.images.base_url + "original"; + var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original"; list.AddRange(GetPosters(response.images).Select(i => new RemoteImageInfo { diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs index d22827c25..36800202f 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs @@ -16,7 +16,7 @@ namespace MediaBrowser.Providers.TV { public abstract class MovieDbProviderBase { - private const string EpisodeUrlPattern = @"http://api.themoviedb.org/3/tv/{0}/season/{1}/episode/{2}?api_key={3}&append_to_response=images,external_ids,credits,videos"; + private const string EpisodeUrlPattern = @"https://api.themoviedb.org/3/tv/{0}/season/{1}/episode/{2}?api_key={3}&append_to_response=images,external_ids,credits,videos"; private readonly IHttpClient _httpClient; private readonly IServerConfigurationManager _configurationManager; private readonly IJsonSerializer _jsonSerializer; diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs index fe0bda828..2e51393e3 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.TV { public class MovieDbSeasonProvider : IRemoteMetadataProvider<Season, SeasonInfo> { - private const string GetTvInfo3 = @"http://api.themoviedb.org/3/tv/{0}/season/{1}?api_key={2}&append_to_response=images,keywords,external_ids,credits,videos"; + private const string GetTvInfo3 = @"https://api.themoviedb.org/3/tv/{0}/season/{1}?api_key={2}&append_to_response=images,keywords,external_ids,credits,videos"; private readonly IHttpClient _httpClient; private readonly IServerConfigurationManager _configurationManager; private readonly IJsonSerializer _jsonSerializer; diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs index f7c19988c..65ac12adf 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs @@ -16,7 +16,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.TV { - public class MovieDbSeriesImageProvider : IRemoteImageProvider, IHasOrder, IHasChangeMonitor + public class MovieDbSeriesImageProvider : IRemoteImageProvider, IHasOrder, IHasItemChangeMonitor { private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; @@ -64,7 +64,7 @@ namespace MediaBrowser.Providers.TV var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - var tmdbImageUrl = tmdbSettings.images.base_url + "original"; + var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original"; list.AddRange(GetPosters(results).Select(i => new RemoteImageInfo { @@ -196,9 +196,9 @@ namespace MediaBrowser.Providers.TV }); } - public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date) + public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) { - return MovieDbSeriesProvider.Current.HasChanged(item, date); + return MovieDbSeriesProvider.Current.HasChanged(item); } } } diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs index 05b1ebc80..7d0f49955 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Providers.TV { public class MovieDbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder { - private const string GetTvInfo3 = @"http://api.themoviedb.org/3/tv/{0}?api_key={1}&append_to_response=credits,images,keywords,external_ids,videos"; + private const string GetTvInfo3 = @"https://api.themoviedb.org/3/tv/{0}?api_key={1}&append_to_response=credits,images,keywords,external_ids,videos,content_ratings"; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); internal static MovieDbSeriesProvider Current { get; private set; } @@ -69,7 +69,7 @@ namespace MediaBrowser.Providers.TV var obj = _jsonSerializer.DeserializeFromFile<RootObject>(dataFilePath); 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 remoteResult = new RemoteSearchResult { @@ -168,7 +168,7 @@ namespace MediaBrowser.Providers.TV { cancellationToken.ThrowIfCancellationRequested(); - result.Item = await FetchMovieData(tmdbId, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + result.Item = await FetchMovieData(tmdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); result.HasMetadata = result.Item != null; } @@ -176,7 +176,7 @@ namespace MediaBrowser.Providers.TV return result; } - private async Task<Series> FetchMovieData(string tmdbId, string language, CancellationToken cancellationToken) + private async Task<Series> FetchMovieData(string tmdbId, string language, string preferredCountryCode, CancellationToken cancellationToken) { string dataFilePath = null; RootObject seriesInfo = null; @@ -201,12 +201,12 @@ namespace MediaBrowser.Providers.TV var item = new Series(); - ProcessMainInfo(item, seriesInfo); + ProcessMainInfo(item, seriesInfo, preferredCountryCode); return item; } - private void ProcessMainInfo(Series series, RootObject seriesInfo) + private void ProcessMainInfo(Series series, RootObject seriesInfo, string preferredCountryCode) { series.Name = seriesInfo.name; series.SetProviderId(MetadataProviders.Tmdb, seriesInfo.id.ToString(_usCulture)); @@ -265,6 +265,26 @@ namespace MediaBrowser.Providers.TV series.SetProviderId(MetadataProviders.Tvdb, ids.tvdb_id.ToString(_usCulture)); } } + + var contentRatings = (seriesInfo.content_ratings ?? new ContentRatings()).results ?? new List<ContentRating>(); + + var ourRelease = contentRatings.FirstOrDefault(c => string.Equals(c.iso_3166_1, preferredCountryCode, StringComparison.OrdinalIgnoreCase)); + var usRelease = contentRatings.FirstOrDefault(c => string.Equals(c.iso_3166_1, "US", StringComparison.OrdinalIgnoreCase)); + var minimumRelease = contentRatings.FirstOrDefault(); + + if (ourRelease != null) + { + series.OfficialRating = ourRelease.rating; + } + else if (usRelease != null) + { + series.OfficialRating = usRelease.rating; + } + else if (minimumRelease != null) + { + series.OfficialRating = minimumRelease.rating; + } + } internal static string GetSeriesDataPath(IApplicationPaths appPaths, string tmdbId) @@ -394,7 +414,7 @@ namespace MediaBrowser.Providers.TV return Path.Combine(path, filename); } - public bool HasChanged(IHasMetadata item, DateTime date) + public bool HasChanged(IHasMetadata item) { if (!MovieDbProvider.Current.GetTheMovieDbOptions().EnableAutomaticUpdates) { @@ -410,7 +430,7 @@ namespace MediaBrowser.Providers.TV var fileInfo = _fileSystem.GetFileInfo(dataFilePath); - return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date; + return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > item.DateLastRefreshed; } return false; @@ -418,7 +438,7 @@ namespace MediaBrowser.Providers.TV private async Task<RemoteSearchResult> FindByExternalId(string id, string externalSource, CancellationToken cancellationToken) { - var url = string.Format("http://api.themoviedb.org/3/tv/find/{0}?api_key={1}&external_source={2}", + var url = string.Format("https://api.themoviedb.org/3/tv/find/{0}?api_key={1}&external_source={2}", id, MovieDbProvider.ApiKey, externalSource); @@ -440,7 +460,7 @@ namespace MediaBrowser.Providers.TV if (tv != null) { 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 remoteResult = new RemoteSearchResult { @@ -481,6 +501,7 @@ namespace MediaBrowser.Providers.TV public class Season { public string air_date { get; set; } + public int episode_count { get; set; } public int id { get; set; } public string poster_path { get; set; } public int season_number { get; set; } @@ -528,7 +549,6 @@ namespace MediaBrowser.Providers.TV public double aspect_ratio { get; set; } public string file_path { get; set; } public int height { get; set; } - public string id { get; set; } public string iso_639_1 { get; set; } public double vote_average { get; set; } public int vote_count { get; set; } @@ -560,6 +580,17 @@ namespace MediaBrowser.Providers.TV public List<object> results { get; set; } } + public class ContentRating + { + public string iso_3166_1 { get; set; } + public string rating { get; set; } + } + + public class ContentRatings + { + public List<ContentRating> results { get; set; } + } + public class RootObject { public string backdrop_path { get; set; } @@ -590,6 +621,7 @@ namespace MediaBrowser.Providers.TV public Keywords keywords { get; set; } public ExternalIds external_ids { get; set; } public Videos videos { get; set; } + public ContentRatings content_ratings { get; set; } } public int Order diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 49d41e06c..ca3320cc6 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -17,7 +17,7 @@ using CommonIO; namespace MediaBrowser.Providers.TV { - public class TvdbEpisodeImageProvider : IRemoteImageProvider, IHasChangeMonitor + public class TvdbEpisodeImageProvider : IRemoteImageProvider, IHasItemChangeMonitor { private readonly IServerConfigurationManager _config; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); @@ -174,7 +174,7 @@ namespace MediaBrowser.Providers.TV }); } - public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date) + public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) { var episode = (Episode)item; @@ -196,7 +196,7 @@ namespace MediaBrowser.Providers.TV // Process images var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(series.ProviderIds, series.GetPreferredMetadataLanguage()); - return _fileSystem.GetLastWriteTimeUtc(seriesXmlPath) > date; + return _fileSystem.GetLastWriteTimeUtc(seriesXmlPath) > item.DateLastRefreshed; } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index 291214fcd..5c88c87d8 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Providers.TV /// <summary> /// Class RemoteEpisodeProvider /// </summary> - class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IItemIdentityProvider<EpisodeInfo>, IHasChangeMonitor + class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IItemIdentityProvider<EpisodeInfo>, IHasItemChangeMonitor { private static readonly string FullIdKey = MetadataProviders.Tvdb + "-Full"; @@ -144,7 +144,7 @@ namespace MediaBrowser.Providers.TV return result; } - public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date) + public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) { // Only enable for virtual items if (item.LocationType != LocationType.Virtual) @@ -160,7 +160,7 @@ namespace MediaBrowser.Providers.TV // Process images var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(series.ProviderIds, series.GetPreferredMetadataLanguage()); - return _fileSystem.GetLastWriteTimeUtc(seriesXmlPath) > date; + return _fileSystem.GetLastWriteTimeUtc(seriesXmlPath) > item.DateLastRefreshed; } return false; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs index 1c83d73fb..672ebc95d 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs @@ -26,12 +26,12 @@ namespace MediaBrowser.Providers.TV /// <summary> /// The server time URL /// </summary> - private const string ServerTimeUrl = "http://thetvdb.com/api/Updates.php?type=none"; + private const string ServerTimeUrl = "https://thetvdb.com/api/Updates.php?type=none"; /// <summary> /// The updates URL /// </summary> - private const string UpdatesUrl = "http://thetvdb.com/api/Updates.php?type=all&time={0}"; + private const string UpdatesUrl = "https://thetvdb.com/api/Updates.php?type=all&time={0}"; /// <summary> /// The _HTTP client diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index 5e7ce9f7e..54d729eb7 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -20,7 +20,7 @@ using CommonIO; namespace MediaBrowser.Providers.TV { - public class TvdbSeasonImageProvider : IRemoteImageProvider, IHasOrder, IHasChangeMonitor + public class TvdbSeasonImageProvider : IRemoteImageProvider, IHasOrder, IHasItemChangeMonitor { private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); @@ -363,7 +363,7 @@ namespace MediaBrowser.Providers.TV }); } - public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date) + public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) { if (item.LocationType != LocationType.Virtual) { @@ -384,7 +384,7 @@ namespace MediaBrowser.Providers.TV var fileInfo = _fileSystem.GetFileInfo(imagesXmlPath); - return fileInfo.Exists && _fileSystem.GetLastWriteTimeUtc(fileInfo) > date; + return fileInfo.Exists && _fileSystem.GetLastWriteTimeUtc(fileInfo) > item.DateLastRefreshed; } return false; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index f66e9254e..ee1505b46 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -53,9 +53,9 @@ namespace MediaBrowser.Providers.TV Current = this; } - private const string SeriesSearchUrl = "http://www.thetvdb.com/api/GetSeries.php?seriesname={0}&language={1}"; - private const string SeriesGetZip = "http://www.thetvdb.com/api/{0}/series/{1}/all/{2}.zip"; - private const string GetSeriesByImdbId = "http://www.thetvdb.com/api/GetSeriesByRemoteID.php?imdbid={0}&language={1}"; + private const string SeriesSearchUrl = "https://www.thetvdb.com/api/GetSeries.php?seriesname={0}&language={1}"; + private const string SeriesGetZip = "https://www.thetvdb.com/api/{0}/series/{1}/all/{2}.zip"; + private const string GetSeriesByImdbId = "https://www.thetvdb.com/api/GetSeriesByRemoteID.php?imdbid={0}&language={1}"; private string NormalizeLanguage(string language) { @@ -1465,4 +1465,4 @@ namespace MediaBrowser.Providers.TV }; } } -}
\ No newline at end of file +} diff --git a/MediaBrowser.Providers/TV/TvExternalIds.cs b/MediaBrowser.Providers/TV/TvExternalIds.cs index 82baae250..f5a26ba0a 100644 --- a/MediaBrowser.Providers/TV/TvExternalIds.cs +++ b/MediaBrowser.Providers/TV/TvExternalIds.cs @@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.TV public string UrlFormatString { - get { return "http://thetvdb.com/index.php?tab=series&id={0}"; } + get { return "https://thetvdb.com/index.php?tab=series&id={0}"; } } public bool Supports(IHasProviderIds item) diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 50ae19580..7adde5c27 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -282,7 +282,7 @@ namespace MediaBrowser.Server.Implementations.Dto else if (dto.HasSyncJob.Value) { - dto.SyncStatus = SyncJobItemStatus.Queued; + dto.SyncStatus = syncProgress.Where(i => string.Equals(i.ItemId, dto.Id, StringComparison.OrdinalIgnoreCase)).Select(i => i.Status).Max(); } } } @@ -307,7 +307,7 @@ namespace MediaBrowser.Server.Implementations.Dto else if (dto.HasSyncJob.Value) { - dto.SyncStatus = SyncJobItemStatus.Queued; + dto.SyncStatus = syncProgress.Where(i => string.Equals(i.ItemId, dto.Id, StringComparison.OrdinalIgnoreCase)).Select(i => i.Status).Max(); } } } @@ -1126,6 +1126,11 @@ namespace MediaBrowser.Server.Implementations.Dto dto.Overview = item.Overview; } + if (fields.Contains(ItemFields.OriginalTitle)) + { + dto.OriginalTitle = item.OriginalTitle; + } + if (fields.Contains(ItemFields.ShortOverview)) { var hasShortOverview = item as IHasShortOverview; diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs index 71019e0ad..e84b66c5a 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs @@ -215,6 +215,12 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications return; } + var video = e.Item as Video; + if (video != null && video.IsThemeMedia) + { + return; + } + var type = GetPlaybackNotificationType(item.MediaType); SendPlaybackNotification(type, e); @@ -230,6 +236,12 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications return; } + var video = e.Item as Video; + if (video != null && video.IsThemeMedia) + { + return; + } + var type = GetPlaybackStoppedNotificationType(item.MediaType); SendPlaybackNotification(type, e); diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index 25463b660..c5cb810e5 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -179,6 +179,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer private void OnWebSocketConnecting(WebSocketConnectingEventArgs args) { + if (_disposed) + { + return; + } + if (WebSocketConnecting != null) { WebSocketConnecting(this, args); @@ -187,6 +192,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer private void OnWebSocketConnected(WebSocketConnectEventArgs args) { + if (_disposed) + { + return; + } + if (WebSocketConnected != null) { WebSocketConnected(this, args); @@ -331,6 +341,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer var httpRes = httpReq.Response; + if (_disposed) + { + httpRes.StatusCode = 503; + httpRes.Close(); + return Task.FromResult(true); + } + var operationName = httpReq.OperationName; var localPath = url.LocalPath; diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 13a06afc2..09ca134d1 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -663,7 +663,7 @@ namespace MediaBrowser.Server.Implementations.IO while (item == null && !string.IsNullOrEmpty(path)) { - item = LibraryManager.FindByPath(path); + item = LibraryManager.FindByPath(path, null); path = Path.GetDirectoryName(path); } diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index ccba293a3..e1bcfa861 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -143,6 +143,7 @@ namespace MediaBrowser.Server.Implementations.Library private readonly Func<ILibraryMonitor> _libraryMonitorFactory; private readonly Func<IProviderManager> _providerManagerFactory; private readonly Func<IUserViewManager> _userviewManager; + public bool IsScanRunning { get; private set; } /// <summary> /// The _library items cache @@ -800,11 +801,12 @@ namespace MediaBrowser.Server.Implementations.Library return _userRootFolder; } - public BaseItem FindByPath(string path) + public BaseItem FindByPath(string path, bool? isFolder) { var query = new InternalItemsQuery { - Path = path + Path = path, + IsFolder = isFolder }; // Only use the database result if there's exactly one item, otherwise we run the risk of returning old data that hasn't been cleaned yet. @@ -1102,6 +1104,7 @@ namespace MediaBrowser.Server.Implementations.Library /// <returns>Task.</returns> public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken) { + IsScanRunning = true; _libraryMonitorFactory().Stop(); try @@ -1111,6 +1114,7 @@ namespace MediaBrowser.Server.Implementations.Library finally { _libraryMonitorFactory().Start(); + IsScanRunning = false; } } diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 95f5cb0e1..092b797ce 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -269,13 +269,15 @@ namespace MediaBrowser.Server.Implementations.Library { var userData = item == null ? new UserItemData() : _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); - SetDefaultAudioStreamIndex(source, userData, user); - SetDefaultSubtitleStreamIndex(source, userData, user); + var allowRememberingSelection = item == null || item.EnableRememberingTrackSelections; + + SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection); + SetDefaultSubtitleStreamIndex(source, userData, user, allowRememberingSelection); } - private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user) + private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection) { - if (userData.SubtitleStreamIndex.HasValue && user.Configuration.RememberSubtitleSelections && user.Configuration.SubtitleMode != SubtitlePlaybackMode.None) + if (userData.SubtitleStreamIndex.HasValue && user.Configuration.RememberSubtitleSelections && user.Configuration.SubtitleMode != SubtitlePlaybackMode.None && allowRememberingSelection) { var index = userData.SubtitleStreamIndex.Value; // Make sure the saved index is still valid @@ -304,9 +306,9 @@ namespace MediaBrowser.Server.Implementations.Library user.Configuration.SubtitleMode, audioLangage); } - private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user) + private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection) { - if (userData.AudioStreamIndex.HasValue && user.Configuration.RememberAudioSelections) + if (userData.AudioStreamIndex.HasValue && user.Configuration.RememberAudioSelections && allowRememberingSelection) { var index = userData.AudioStreamIndex.Value; // Make sure the saved index is still valid diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 3c7ee55b7..54443d9ca 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -42,10 +42,21 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _logger.Info("Copying recording stream to file {0}", targetFile); - var durationToken = new CancellationTokenSource(duration); - var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; + if (mediaSource.RunTimeTicks.HasValue) + { + // The media source already has a fixed duration + // But add another stop 1 minute later just in case the recording gets stuck for any reason + var durationToken = new CancellationTokenSource(duration.Add(TimeSpan.FromMinutes(1))); + cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; + } + else + { + // The media source if infinite so we need to handle stopping ourselves + var durationToken = new CancellationTokenSource(duration); + cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; + } - await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken).ConfigureAwait(false); + await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 60ff23b04..351bc05af 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -591,7 +591,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV throw new ApplicationException("Tuner not found."); } - private async Task<Tuple<MediaSourceInfo, SemaphoreSlim>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) + private async Task<Tuple<MediaSourceInfo, ITunerHost, SemaphoreSlim>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) { _logger.Info("Streaming Channel " + channelId); @@ -599,7 +599,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { try { - return await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); + var result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); + + return new Tuple<MediaSourceInfo, ITunerHost, SemaphoreSlim>(result.Item1, hostInstance, result.Item2); } catch (Exception e) { @@ -797,8 +799,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg //await Task.Delay(3000, cancellationToken).ConfigureAwait(false); - var duration = recordingEndDate - DateTime.UtcNow; - var recorder = await GetRecorder().ConfigureAwait(false); if (recorder is EncodedRecorder) @@ -816,6 +816,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV recording.DateLastUpdated = DateTime.UtcNow; _recordingProvider.AddOrUpdate(recording); + var duration = recordingEndDate - DateTime.UtcNow; + _logger.Info("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture)); _logger.Info("Writing file to path: " + recordPath); @@ -823,10 +825,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV Action onStarted = () => { - result.Item2.Release(); + result.Item3.Release(); isResourceOpen = false; }; + var pathWithDuration = result.Item2.ApplyDuration(mediaStreamInfo.Path, duration); + + // If it supports supplying duration via url + if (!string.Equals(pathWithDuration, mediaStreamInfo.Path, StringComparison.OrdinalIgnoreCase)) + { + mediaStreamInfo.Path = pathWithDuration; + mediaStreamInfo.RunTimeTicks = duration.Ticks; + } + await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false); recording.Status = RecordingStatus.Completed; @@ -836,7 +847,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { if (isResourceOpen) { - result.Item2.Release(); + result.Item3.Release(); } _libraryMonitor.ReportFileSystemChangeComplete(recordPath, false); @@ -916,13 +927,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private async Task<IRecorder> GetRecorder() { - if (GetConfiguration().EnableRecordingEncoding) + var config = GetConfiguration(); + + if (config.EnableRecordingEncoding) { var regInfo = await _security.GetRegistrationStatus("embytvrecordingconversion").ConfigureAwait(false); if (regInfo.IsValid) { - return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer); + return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, config); } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 442f151dd..d589b2bc3 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -12,6 +12,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; @@ -23,6 +24,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private readonly IFileSystem _fileSystem; private readonly IMediaEncoder _mediaEncoder; private readonly IApplicationPaths _appPaths; + private readonly LiveTvOptions _liveTvOptions; private bool _hasExited; private Stream _logFileStream; private string _targetPath; @@ -30,17 +32,32 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private readonly IJsonSerializer _json; private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>(); - public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IApplicationPaths appPaths, IJsonSerializer json) + public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IApplicationPaths appPaths, IJsonSerializer json, LiveTvOptions liveTvOptions) { _logger = logger; _fileSystem = fileSystem; _mediaEncoder = mediaEncoder; _appPaths = appPaths; _json = json; + _liveTvOptions = liveTvOptions; } public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { + if (mediaSource.RunTimeTicks.HasValue) + { + // The media source already has a fixed duration + // But add another stop 1 minute later just in case the recording gets stuck for any reason + var durationToken = new CancellationTokenSource(duration.Add(TimeSpan.FromMinutes(1))); + cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; + } + else + { + // The media source if infinite so we need to handle stopping ourselves + var durationToken = new CancellationTokenSource(duration); + cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; + } + _targetPath = targetFile; _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile)); @@ -129,7 +146,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { var copyAudio = new[] { "aac", "mp3" }; var mediaStreams = mediaSource.MediaStreams ?? new List<MediaStream>(); - if (mediaStreams.Any(i => i.Type == MediaStreamType.Audio && copyAudio.Contains(i.Codec, StringComparer.OrdinalIgnoreCase))) + if (_liveTvOptions.EnableOriginalAudioWithEncodedRecordings || mediaStreams.Any(i => i.Type == MediaStreamType.Audio && copyAudio.Contains(i.Codec, StringComparer.OrdinalIgnoreCase))) { return "-codec:a:0 copy"; } @@ -140,7 +157,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { audioChannels = audioStream.Channels ?? audioChannels; } - return "-codec:a:0 aac -strict experimental -ab 320000"; + return "-codec:a:0 aac -strict experimental -ab 320000 -af \"async=1000\""; } private bool EncodeVideo(MediaSourceInfo mediaSource) diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 02a8d6938..9bb5b4fd7 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -224,7 +224,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); - //await AddMediaInfo(stream, false, resourcePool, cancellationToken).ConfigureAwait(false); + if (EnableMediaProbing) + { + await AddMediaInfo(stream, false, resourcePool, cancellationToken).ConfigureAwait(false); + } + return new Tuple<MediaSourceInfo, SemaphoreSlim>(stream, resourcePool); } catch (Exception ex) @@ -239,6 +243,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts throw new LiveTvConflictException(); } + protected virtual bool EnableMediaProbing + { + get { return false; } + } + protected async Task<bool> IsAvailable(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) { try @@ -268,6 +277,25 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts return _semaphoreLocks.GetOrAdd(url, key => new SemaphoreSlim(1, 1)); } + private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, SemaphoreSlim resourcePool, CancellationToken cancellationToken) + { + await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + await AddMediaInfoInternal(mediaSource, isAudio, cancellationToken).ConfigureAwait(false); + + // Leave the resource locked. it will be released upstream + } + catch (Exception) + { + // Release the resource if there's some kind of failure. + resourcePool.Release(); + + throw; + } + } + private async Task AddMediaInfoInternal(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken) { var originalRuntime = mediaSource.RunTimeTicks; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 469767c65..a3e5589e8 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -59,6 +59,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun return id; } + public string ApplyDuration(string streamPath, TimeSpan duration) + { + streamPath += streamPath.IndexOf('?') == -1 ? "?" : "&"; + streamPath += "duration=" + Convert.ToInt32(duration.TotalSeconds).ToString(CultureInfo.InvariantCulture); + + return streamPath; + } + private async Task<IEnumerable<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken) { var options = new HttpRequestOptions diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 523f14dfc..c874e51b6 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -146,5 +146,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { return Task.FromResult(true); } + + public string ApplyDuration(string streamPath, TimeSpan duration) + { + return streamPath; + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs index ffd85fd18..1e571c84f 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs @@ -164,5 +164,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp return list; } + + public string ApplyDuration(string streamPath, TimeSpan duration) + { + return streamPath; + } } } diff --git a/MediaBrowser.Server.Implementations/Localization/Core/en-US.json b/MediaBrowser.Server.Implementations/Localization/Core/en-US.json index 0d5b5c4aa..5e2f98c09 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/en-US.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/en-US.json @@ -1,178 +1,179 @@ { - "DbUpgradeMessage": "Please wait while your Emby Server database is upgraded. {0}% complete.", - "AppDeviceValues": "App: {0}, Device: {1}", - "UserDownloadingItemWithValues": "{0} is downloading {1}", - "FolderTypeMixed": "Mixed content", - "FolderTypeMovies": "Movies", - "FolderTypeMusic": "Music", - "FolderTypeAdultVideos": "Adult videos", - "FolderTypePhotos": "Photos", - "FolderTypeMusicVideos": "Music videos", - "FolderTypeHomeVideos": "Home videos", - "FolderTypeGames": "Games", - "FolderTypeBooks": "Books", - "FolderTypeTvShows": "TV", - "FolderTypeInherit": "Inherit", - "HeaderCastCrew": "Cast & Crew", - "HeaderPeople": "People", - "ValueSpecialEpisodeName": "Special - {0}", - "LabelChapterName": "Chapter {0}", - "NameSeasonNumber": "Season {0}", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelGithub": "Github", - "LabelApiDocumentation": "Api Documentation", - "LabelDeveloperResources": "Developer Resources", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server", - "CategorySync": "Sync", - "CategoryUser": "User", - "CategorySystem": "System", - "CategoryApplication": "Application", - "CategoryPlugin": "Plugin", - "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionApplicationUpdateAvailable": "Application update available", - "NotificationOptionApplicationUpdateInstalled": "Application update installed", - "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionPluginInstalled": "Plugin installed", - "NotificationOptionPluginUninstalled": "Plugin uninstalled", - "NotificationOptionVideoPlayback": "Video playback started", - "NotificationOptionAudioPlayback": "Audio playback started", - "NotificationOptionGamePlayback": "Game playback started", - "NotificationOptionVideoPlaybackStopped": "Video playback stopped", - "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", - "NotificationOptionGamePlaybackStopped": "Game playback stopped", - "NotificationOptionTaskFailed": "Scheduled task failure", - "NotificationOptionInstallationFailed": "Installation failure", - "NotificationOptionNewLibraryContent": "New content added", - "NotificationOptionNewLibraryContentMultiple": "New content added (multiple)", - "NotificationOptionCameraImageUploaded": "Camera image uploaded", - "NotificationOptionUserLockedOut": "User locked out", - "NotificationOptionServerRestartRequired": "Server restart required", - "ViewTypePlaylists": "Playlists", - "ViewTypeMovies": "Movies", - "ViewTypeTvShows": "TV", - "ViewTypeGames": "Games", - "ViewTypeMusic": "Music", - "ViewTypeMusicGenres": "Genres", - "ViewTypeMusicArtists": "Artists", - "ViewTypeBoxSets": "Collections", - "ViewTypeChannels": "Channels", - "ViewTypeLiveTV": "Live TV", - "ViewTypeLiveTvNowPlaying": "Now Airing", - "ViewTypeLatestGames": "Latest Games", - "ViewTypeRecentlyPlayedGames": "Recently Played", - "ViewTypeGameFavorites": "Favorites", - "ViewTypeGameSystems": "Game Systems", - "ViewTypeGameGenres": "Genres", - "ViewTypeTvResume": "Resume", - "ViewTypeTvNextUp": "Next Up", - "ViewTypeTvLatest": "Latest", - "ViewTypeTvShowSeries": "Series", - "ViewTypeTvGenres": "Genres", - "ViewTypeTvFavoriteSeries": "Favorite Series", - "ViewTypeTvFavoriteEpisodes": "Favorite Episodes", - "ViewTypeMovieResume": "Resume", - "ViewTypeMovieLatest": "Latest", - "ViewTypeMovieMovies": "Movies", - "ViewTypeMovieCollections": "Collections", - "ViewTypeMovieFavorites": "Favorites", - "ViewTypeMovieGenres": "Genres", - "ViewTypeMusicLatest": "Latest", - "ViewTypeMusicPlaylists": "Playlists", - "ViewTypeMusicAlbums": "Albums", - "ViewTypeMusicAlbumArtists": "Album Artists", - "HeaderOtherDisplaySettings": "Display Settings", - "ViewTypeMusicSongs": "Songs", - "ViewTypeMusicFavorites": "Favorites", - "ViewTypeMusicFavoriteAlbums": "Favorite Albums", - "ViewTypeMusicFavoriteArtists": "Favorite Artists", - "ViewTypeMusicFavoriteSongs": "Favorite Songs", - "ViewTypeFolders": "Folders", - "ViewTypeLiveTvRecordingGroups": "Recordings", - "ViewTypeLiveTvChannels": "Channels", - "ScheduledTaskFailedWithName": "{0} failed", - "LabelRunningTimeValue": "Running time: {0}", - "ScheduledTaskStartedWithName": "{0} started", - "VersionNumber": "Version {0}", - "PluginInstalledWithName": "{0} was installed", - "PluginUpdatedWithName": "{0} was updated", - "PluginUninstalledWithName": "{0} was uninstalled", - "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip address: {0}", - "DeviceOnlineWithName": "{0} is connected", - "UserOnlineFromDevice": "{0} is online from {1}", - "ProviderValue": "Provider: {0}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", - "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}", - "UserCreatedWithName": "User {0} has been created", - "UserPasswordChangedWithName": "Password has been changed for user {0}", - "UserDeletedWithName": "User {0} has been deleted", - "MessageServerConfigurationUpdated": "Server configuration has been updated", - "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", - "MessageApplicationUpdated": "Emby Server has been updated", - "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", - "DeviceOfflineWithName": "{0} has disconnected", - "UserLockedOutWithName": "User {0} has been locked out", - "UserOfflineFromDevice": "{0} has disconnected from {1}", - "UserStartedPlayingItemWithValues": "{0} has started playing {1}", - "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", - "HeaderUnidentified": "Unidentified", - "HeaderImagePrimary": "Primary", - "HeaderImageBackdrop": "Backdrop", - "HeaderImageLogo": "Logo", - "HeaderUserPrimaryImage": "User Image", - "HeaderOverview": "Overview", - "HeaderShortOverview": "Short Overview", - "HeaderType": "Type", - "HeaderSeverity": "Severity", - "HeaderUser": "User", - "HeaderName": "Name", - "HeaderDate": "Date", - "HeaderPremiereDate": "Premiere Date", - "HeaderDateAdded": "Date Added", - "HeaderReleaseDate": "Release date", - "HeaderRuntime": "Runtime", - "HeaderPlayCount": "Play Count", - "HeaderSeason": "Season", - "HeaderSeasonNumber": "Season number", - "HeaderSeries": "Series:", - "HeaderNetwork": "Network", - "HeaderYear": "Year:", - "HeaderYears": "Years:", - "HeaderParentalRating": "Parental Rating", - "HeaderCommunityRating": "Community rating", - "HeaderTrailers": "Trailers", - "HeaderSpecials": "Specials", - "HeaderGameSystems": "Game Systems", - "HeaderPlayers": "Players:", - "HeaderAlbumArtists": "Album Artists", - "HeaderAlbums": "Albums", - "HeaderDisc": "Disc", - "HeaderTrack": "Track", - "HeaderAudio": "Audio", - "HeaderVideo": "Video", - "HeaderEmbeddedImage": "Embedded image", - "HeaderResolution": "Resolution", - "HeaderSubtitles": "Subtitles", - "HeaderGenres": "Genres", - "HeaderCountries": "Countries", - "HeaderStatus": "Status", - "HeaderTracks": "Tracks", - "HeaderMusicArtist": "Music artist", - "HeaderLocked": "Locked", - "HeaderStudios": "Studios", - "HeaderActor": "Actors", - "HeaderComposer": "Composers", - "HeaderDirector": "Directors", - "HeaderGuestStar": "Guest star", - "HeaderProducer": "Producers", - "HeaderWriter": "Writers", - "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings", - "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." + "DbUpgradeMessage": "Please wait while your Emby Server database is upgraded. {0}% complete.", + "AppDeviceValues": "App: {0}, Device: {1}", + "UserDownloadingItemWithValues": "{0} is downloading {1}", + "FolderTypeMixed": "Mixed content", + "FolderTypeMovies": "Movies", + "FolderTypeMusic": "Music", + "FolderTypeAdultVideos": "Adult videos", + "FolderTypePhotos": "Photos", + "FolderTypeMusicVideos": "Music videos", + "FolderTypeHomeVideos": "Home videos", + "FolderTypeGames": "Games", + "FolderTypeBooks": "Books", + "FolderTypeTvShows": "TV", + "FolderTypeInherit": "Inherit", + "HeaderCastCrew": "Cast & Crew", + "HeaderPeople": "People", + "ValueSpecialEpisodeName": "Special - {0}", + "LabelChapterName": "Chapter {0}", + "NameSeasonUnknown": "Season Unknown", + "NameSeasonNumber": "Season {0}", + "LabelExit": "Exit", + "LabelVisitCommunity": "Visit Community", + "LabelGithub": "Github", + "LabelApiDocumentation": "Api Documentation", + "LabelDeveloperResources": "Developer Resources", + "LabelBrowseLibrary": "Browse Library", + "LabelConfigureServer": "Configure Emby", + "LabelRestartServer": "Restart Server", + "CategorySync": "Sync", + "CategoryUser": "User", + "CategorySystem": "System", + "CategoryApplication": "Application", + "CategoryPlugin": "Plugin", + "NotificationOptionPluginError": "Plugin failure", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback started", + "NotificationOptionAudioPlayback": "Audio playback started", + "NotificationOptionGamePlayback": "Game playback started", + "NotificationOptionVideoPlaybackStopped": "Video playback stopped", + "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", + "NotificationOptionGamePlaybackStopped": "Game playback stopped", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "NotificationOptionNewLibraryContentMultiple": "New content added (multiple)", + "NotificationOptionCameraImageUploaded": "Camera image uploaded", + "NotificationOptionUserLockedOut": "User locked out", + "NotificationOptionServerRestartRequired": "Server restart required", + "ViewTypePlaylists": "Playlists", + "ViewTypeMovies": "Movies", + "ViewTypeTvShows": "TV", + "ViewTypeGames": "Games", + "ViewTypeMusic": "Music", + "ViewTypeMusicGenres": "Genres", + "ViewTypeMusicArtists": "Artists", + "ViewTypeBoxSets": "Collections", + "ViewTypeChannels": "Channels", + "ViewTypeLiveTV": "Live TV", + "ViewTypeLiveTvNowPlaying": "Now Airing", + "ViewTypeLatestGames": "Latest Games", + "ViewTypeRecentlyPlayedGames": "Recently Played", + "ViewTypeGameFavorites": "Favorites", + "ViewTypeGameSystems": "Game Systems", + "ViewTypeGameGenres": "Genres", + "ViewTypeTvResume": "Resume", + "ViewTypeTvNextUp": "Next Up", + "ViewTypeTvLatest": "Latest", + "ViewTypeTvShowSeries": "Series", + "ViewTypeTvGenres": "Genres", + "ViewTypeTvFavoriteSeries": "Favorite Series", + "ViewTypeTvFavoriteEpisodes": "Favorite Episodes", + "ViewTypeMovieResume": "Resume", + "ViewTypeMovieLatest": "Latest", + "ViewTypeMovieMovies": "Movies", + "ViewTypeMovieCollections": "Collections", + "ViewTypeMovieFavorites": "Favorites", + "ViewTypeMovieGenres": "Genres", + "ViewTypeMusicLatest": "Latest", + "ViewTypeMusicPlaylists": "Playlists", + "ViewTypeMusicAlbums": "Albums", + "ViewTypeMusicAlbumArtists": "Album Artists", + "HeaderOtherDisplaySettings": "Display Settings", + "ViewTypeMusicSongs": "Songs", + "ViewTypeMusicFavorites": "Favorites", + "ViewTypeMusicFavoriteAlbums": "Favorite Albums", + "ViewTypeMusicFavoriteArtists": "Favorite Artists", + "ViewTypeMusicFavoriteSongs": "Favorite Songs", + "ViewTypeFolders": "Folders", + "ViewTypeLiveTvRecordingGroups": "Recordings", + "ViewTypeLiveTvChannels": "Channels", + "ScheduledTaskFailedWithName": "{0} failed", + "LabelRunningTimeValue": "Running time: {0}", + "ScheduledTaskStartedWithName": "{0} started", + "VersionNumber": "Version {0}", + "PluginInstalledWithName": "{0} was installed", + "PluginUpdatedWithName": "{0} was updated", + "PluginUninstalledWithName": "{0} was uninstalled", + "ItemAddedWithName": "{0} was added to the library", + "ItemRemovedWithName": "{0} was removed from the library", + "LabelIpAddressValue": "Ip address: {0}", + "DeviceOnlineWithName": "{0} is connected", + "UserOnlineFromDevice": "{0} is online from {1}", + "ProviderValue": "Provider: {0}", + "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", + "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}", + "UserCreatedWithName": "User {0} has been created", + "UserPasswordChangedWithName": "Password has been changed for user {0}", + "UserDeletedWithName": "User {0} has been deleted", + "MessageServerConfigurationUpdated": "Server configuration has been updated", + "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", + "MessageApplicationUpdated": "Emby Server has been updated", + "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", + "AuthenticationSucceededWithUserName": "{0} successfully authenticated", + "DeviceOfflineWithName": "{0} has disconnected", + "UserLockedOutWithName": "User {0} has been locked out", + "UserOfflineFromDevice": "{0} has disconnected from {1}", + "UserStartedPlayingItemWithValues": "{0} has started playing {1}", + "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}", + "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", + "HeaderUnidentified": "Unidentified", + "HeaderImagePrimary": "Primary", + "HeaderImageBackdrop": "Backdrop", + "HeaderImageLogo": "Logo", + "HeaderUserPrimaryImage": "User Image", + "HeaderOverview": "Overview", + "HeaderShortOverview": "Short Overview", + "HeaderType": "Type", + "HeaderSeverity": "Severity", + "HeaderUser": "User", + "HeaderName": "Name", + "HeaderDate": "Date", + "HeaderPremiereDate": "Premiere Date", + "HeaderDateAdded": "Date Added", + "HeaderReleaseDate": "Release date", + "HeaderRuntime": "Runtime", + "HeaderPlayCount": "Play Count", + "HeaderSeason": "Season", + "HeaderSeasonNumber": "Season number", + "HeaderSeries": "Series:", + "HeaderNetwork": "Network", + "HeaderYear": "Year:", + "HeaderYears": "Years:", + "HeaderParentalRating": "Parental Rating", + "HeaderCommunityRating": "Community rating", + "HeaderTrailers": "Trailers", + "HeaderSpecials": "Specials", + "HeaderGameSystems": "Game Systems", + "HeaderPlayers": "Players:", + "HeaderAlbumArtists": "Album Artists", + "HeaderAlbums": "Albums", + "HeaderDisc": "Disc", + "HeaderTrack": "Track", + "HeaderAudio": "Audio", + "HeaderVideo": "Video", + "HeaderEmbeddedImage": "Embedded image", + "HeaderResolution": "Resolution", + "HeaderSubtitles": "Subtitles", + "HeaderGenres": "Genres", + "HeaderCountries": "Countries", + "HeaderStatus": "Status", + "HeaderTracks": "Tracks", + "HeaderMusicArtist": "Music artist", + "HeaderLocked": "Locked", + "HeaderStudios": "Studios", + "HeaderActor": "Actors", + "HeaderComposer": "Composers", + "HeaderDirector": "Directors", + "HeaderGuestStar": "Guest star", + "HeaderProducer": "Producers", + "HeaderWriter": "Writers", + "HeaderParentalRatings": "Parental Ratings", + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 60d8f737f..43f5d741b 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -68,9 +68,13 @@ <Reference Include="ServiceStack.Api.Swagger"> <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Api.Swagger.dll</HintPath> </Reference> - <Reference Include="SocketHttpListener, Version=1.0.5908.28560, Culture=neutral, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\SocketHttpListener.1.0.0.29\lib\net45\SocketHttpListener.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="SocketHttpListener, Version=1.0.5955.1537, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\SocketHttpListener.1.0.0.30\lib\net45\SocketHttpListener.dll</HintPath> + <Private>True</Private> </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> diff --git a/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs b/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs index debcd054f..76682c63b 100644 --- a/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs +++ b/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs @@ -26,6 +26,38 @@ namespace MediaBrowser.Server.Implementations.Persistence AddCodecTagColumn(); AddCommentColumn(); AddNalColumn(); + AddIsAvcColumn(); + } + + private void AddIsAvcColumn() + { + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "PRAGMA table_info(mediastreams)"; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + if (!reader.IsDBNull(1)) + { + var name = reader.GetString(1); + + if (string.Equals(name, "IsAvc", StringComparison.OrdinalIgnoreCase)) + { + return; + } + } + } + } + } + + var builder = new StringBuilder(); + + builder.AppendLine("alter table mediastreams"); + builder.AppendLine("add column IsAvc BIT NULL"); + + _connection.RunQueries(new[] { builder.ToString() }, _logger); } private void AddNalColumn() diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 25582852f..a44d66c73 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -18,6 +18,7 @@ using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Playlists; using MediaBrowser.Model.LiveTv; @@ -80,7 +81,7 @@ namespace MediaBrowser.Server.Implementations.Persistence private IDbCommand _updateInheritedRatingCommand; private IDbCommand _updateInheritedTagsCommand; - private const int LatestSchemaVersion = 64; + private const int LatestSchemaVersion = 65; /// <summary> /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class. @@ -124,7 +125,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection = await SqliteExtensions.ConnectToDb(dbFile, Logger).ConfigureAwait(false); var createMediaStreamsTableCommand - = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))"; + = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, PRIMARY KEY (ItemId, StreamIndex))"; string[] queries = { @@ -226,6 +227,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection.AddColumn(Logger, "TypedBaseItems", "CriticRatingSummary", "Text"); _connection.AddColumn(Logger, "TypedBaseItems", "DateModifiedDuringLastRefresh", "DATETIME"); _connection.AddColumn(Logger, "TypedBaseItems", "InheritedTags", "Text"); + _connection.AddColumn(Logger, "TypedBaseItems", "CleanName", "Text"); PrepareStatements(); @@ -391,7 +393,8 @@ namespace MediaBrowser.Server.Implementations.Persistence "RefFrames", "CodecTag", "Comment", - "NalLengthSize" + "NalLengthSize", + "IsAvc" }; /// <summary> @@ -465,7 +468,8 @@ namespace MediaBrowser.Server.Implementations.Persistence "CriticRating", "CriticRatingSummary", "DateModifiedDuringLastRefresh", - "InheritedTags" + "InheritedTags", + "CleanName" }; _saveItemCommand = _connection.CreateCommand(); _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values ("; @@ -791,6 +795,15 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveItemCommand.GetParameter(index++).Value = null; } + if (string.IsNullOrWhiteSpace(item.Name)) + { + _saveItemCommand.GetParameter(index++).Value = null; + } + else + { + _saveItemCommand.GetParameter(index++).Value = item.Name.RemoveDiacritics(); + } + _saveItemCommand.Transaction = transaction; _saveItemCommand.ExecuteNonQuery(); @@ -1985,7 +1998,7 @@ namespace MediaBrowser.Server.Implementations.Persistence if (!string.IsNullOrWhiteSpace(query.NameContains)) { - whereClauses.Add("Name like @NameContains"); + whereClauses.Add("CleanName like @NameContains"); cmd.Parameters.Add(cmd, "@NameContains", DbType.String).Value = "%" + query.NameContains + "%"; } @@ -2883,6 +2896,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveStreamCommand.GetParameter(index++).Value = stream.CodecTag; _saveStreamCommand.GetParameter(index++).Value = stream.Comment; _saveStreamCommand.GetParameter(index++).Value = stream.NalLengthSize; + _saveStreamCommand.GetParameter(index++).Value = stream.IsAVC; _saveStreamCommand.Transaction = transaction; _saveStreamCommand.ExecuteNonQuery(); @@ -3046,6 +3060,11 @@ namespace MediaBrowser.Server.Implementations.Persistence item.NalLengthSize = reader.GetString(27); } + if (!reader.IsDBNull(28)) + { + item.IsAVC = reader.GetBoolean(28); + } + return item; } diff --git a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs index 4a69646f8..acb1227e4 100644 --- a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs +++ b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs @@ -17,7 +17,7 @@ using MediaBrowser.Model.Configuration; namespace MediaBrowser.Server.Implementations.Photos { - public abstract class BaseDynamicImageProvider<T> : IHasChangeMonitor, IForcedProvider, ICustomMetadataProvider<T>, IHasOrder + public abstract class BaseDynamicImageProvider<T> : IHasItemChangeMonitor, IForcedProvider, ICustomMetadataProvider<T>, IHasOrder where T : IHasMetadata { protected IFileSystem FileSystem { get; private set; } @@ -247,7 +247,7 @@ namespace MediaBrowser.Server.Implementations.Photos get { return 7; } } - public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date) + public bool HasChanged(IHasMetadata item, IDirectoryService directoryServicee) { if (!Supports(item)) { diff --git a/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs b/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs index 8719f5448..33d106916 100644 --- a/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs +++ b/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs @@ -71,6 +71,8 @@ namespace MediaBrowser.Server.Implementations.ServerManager /// <value>The web socket listeners.</value> private readonly List<IWebSocketListener> _webSocketListeners = new List<IWebSocketListener>(); + private bool _disposed; + /// <summary> /// Initializes a new instance of the <see cref="ServerManager" /> class. /// </summary> @@ -143,6 +145,11 @@ namespace MediaBrowser.Server.Implementations.ServerManager /// <param name="e">The <see cref="WebSocketConnectEventArgs" /> instance containing the event data.</param> void HttpServer_WebSocketConnected(object sender, WebSocketConnectEventArgs e) { + if (_disposed) + { + return; + } + var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger) { OnReceive = ProcessWebSocketMessageReceived, @@ -164,6 +171,11 @@ namespace MediaBrowser.Server.Implementations.ServerManager /// <param name="result">The result.</param> private async void ProcessWebSocketMessageReceived(WebSocketMessageInfo result) { + if (_disposed) + { + return; + } + //_logger.Debug("Websocket message received: {0}", result.MessageType); var tasks = _webSocketListeners.Select(i => Task.Run(async () => @@ -244,6 +256,11 @@ namespace MediaBrowser.Server.Implementations.ServerManager throw new ArgumentNullException("dataFunction"); } + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + cancellationToken.ThrowIfCancellationRequested(); var connectionsList = connections.Where(s => s.State == WebSocketState.Open).ToList(); @@ -301,6 +318,8 @@ namespace MediaBrowser.Server.Implementations.ServerManager /// </summary> public void Dispose() { + _disposed = true; + Dispose(true); GC.SuppressFinalize(this); } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs index 33874b4d4..d95a4fefb 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs @@ -149,6 +149,11 @@ namespace MediaBrowser.Server.Implementations.Sync { var job = _syncRepo.GetJob(id); + if (job == null) + { + return Task.FromResult(true); + } + var result = _syncManager.GetJobItems(new SyncJobItemQuery { JobId = job.Id, @@ -577,7 +582,7 @@ namespace MediaBrowser.Server.Implementations.Sync conversionOptions.ItemId = item.Id.ToString("N"); conversionOptions.MediaSources = _mediaSourceManager.GetStaticMediaSources(item, false, user).ToList(); - var streamInfo = new StreamBuilder(_logger).BuildVideoItem(conversionOptions); + var streamInfo = new StreamBuilder(_mediaEncoder, _logger).BuildVideoItem(conversionOptions); var mediaSource = streamInfo.MediaSource; // No sense creating external subs if we're already burning one into the video @@ -632,6 +637,7 @@ namespace MediaBrowser.Server.Implementations.Sync }, innerProgress, cancellationToken); + jobItem.ItemDateModifiedTicks = item.DateModified.Ticks; _syncManager.OnConversionComplete(jobItem); } catch (OperationCanceledException) @@ -668,6 +674,7 @@ namespace MediaBrowser.Server.Implementations.Sync throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol)); } + jobItem.ItemDateModifiedTicks = item.DateModified.Ticks; jobItem.MediaSource = mediaSource; } @@ -779,7 +786,7 @@ namespace MediaBrowser.Server.Implementations.Sync conversionOptions.ItemId = item.Id.ToString("N"); conversionOptions.MediaSources = _mediaSourceManager.GetStaticMediaSources(item, false, user).ToList(); - var streamInfo = new StreamBuilder(_logger).BuildAudioItem(conversionOptions); + var streamInfo = new StreamBuilder(_mediaEncoder, _logger).BuildAudioItem(conversionOptions); var mediaSource = streamInfo.MediaSource; jobItem.MediaSourceId = streamInfo.MediaSourceId; @@ -819,6 +826,7 @@ namespace MediaBrowser.Server.Implementations.Sync }, innerProgress, cancellationToken); + jobItem.ItemDateModifiedTicks = item.DateModified.Ticks; _syncManager.OnConversionComplete(jobItem); } catch (OperationCanceledException) @@ -855,6 +863,7 @@ namespace MediaBrowser.Server.Implementations.Sync throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol)); } + jobItem.ItemDateModifiedTicks = item.DateModified.Ticks; jobItem.MediaSource = mediaSource; } @@ -871,6 +880,7 @@ namespace MediaBrowser.Server.Implementations.Sync jobItem.Progress = 50; jobItem.Status = SyncJobItemStatus.ReadyToTransfer; + jobItem.ItemDateModifiedTicks = item.DateModified.Ticks; await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); } @@ -880,6 +890,7 @@ namespace MediaBrowser.Server.Implementations.Sync jobItem.Progress = 50; jobItem.Status = SyncJobItemStatus.ReadyToTransfer; + jobItem.ItemDateModifiedTicks = item.DateModified.Ticks; await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index 044c8b93a..fe1a7fb28 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -775,6 +775,13 @@ namespace MediaBrowser.Server.Implementations.Sync removeFromDevice = true; } } + else if (libraryItem != null && libraryItem.DateModified.Ticks != jobItem.ItemDateModifiedTicks && jobItem.ItemDateModifiedTicks > 0) + { + _logger.Info("Setting status to Queued for {0} because the media has been modified since the original sync.", jobItem.ItemId); + jobItem.Status = SyncJobItemStatus.Queued; + jobItem.Progress = 0; + requiresSaving = true; + } } else { @@ -881,6 +888,13 @@ namespace MediaBrowser.Server.Implementations.Sync removeFromDevice = true; } } + else if (libraryItem != null && libraryItem.DateModified.Ticks != jobItem.ItemDateModifiedTicks && jobItem.ItemDateModifiedTicks > 0) + { + _logger.Info("Setting status to Queued for {0} because the media has been modified since the original sync.", jobItem.ItemId); + jobItem.Status = SyncJobItemStatus.Queued; + jobItem.Progress = 0; + requiresSaving = true; + } } else { @@ -1117,7 +1131,7 @@ namespace MediaBrowser.Server.Implementations.Sync public SyncJobOptions GetAudioOptions(SyncJobItem jobItem, SyncJob job) { var options = GetSyncJobOptions(jobItem.TargetId, null, null); - + if (job.Bitrate.HasValue) { options.DeviceProfile.MaxStaticBitrate = job.Bitrate.Value; diff --git a/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs b/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs index 464e8aa58..965c081eb 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs @@ -50,7 +50,7 @@ namespace MediaBrowser.Server.Implementations.Sync "create table if not exists SyncJobs (Id GUID PRIMARY KEY, TargetId TEXT NOT NULL, Name TEXT NOT NULL, Profile TEXT, Quality TEXT, Bitrate INT, Status TEXT NOT NULL, Progress FLOAT, UserId TEXT NOT NULL, ItemIds TEXT NOT NULL, Category TEXT, ParentId TEXT, UnwatchedOnly BIT, ItemLimit INT, SyncNewContent BIT, DateCreated DateTime, DateLastModified DateTime, ItemCount int)", "create index if not exists idx_SyncJobs on SyncJobs(Id)", - "create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, ItemName TEXT, MediaSourceId TEXT, JobId TEXT, TemporaryPath TEXT, OutputPath TEXT, Status TEXT, TargetId TEXT, DateCreated DateTime, Progress FLOAT, AdditionalFiles TEXT, MediaSource TEXT, IsMarkedForRemoval BIT, JobItemIndex INT)", + "create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, ItemName TEXT, MediaSourceId TEXT, JobId TEXT, TemporaryPath TEXT, OutputPath TEXT, Status TEXT, TargetId TEXT, DateCreated DateTime, Progress FLOAT, AdditionalFiles TEXT, MediaSource TEXT, IsMarkedForRemoval BIT, JobItemIndex INT, ItemDateModifiedTicks BIGINT)", "create index if not exists idx_SyncJobItems on SyncJobs(Id)", //pragmas @@ -63,6 +63,7 @@ namespace MediaBrowser.Server.Implementations.Sync _connection.AddColumn(Logger, "SyncJobs", "Profile", "TEXT"); _connection.AddColumn(Logger, "SyncJobs", "Bitrate", "INT"); + _connection.AddColumn(Logger, "SyncJobItems", "ItemDateModifiedTicks", "BIGINT"); PrepareStatements(); } @@ -127,7 +128,7 @@ namespace MediaBrowser.Server.Implementations.Sync // _insertJobItemCommand _insertJobItemCommand = _connection.CreateCommand(); - _insertJobItemCommand.CommandText = "insert into SyncJobItems (Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource, IsMarkedForRemoval, JobItemIndex) values (@Id, @ItemId, @ItemName, @MediaSourceId, @JobId, @TemporaryPath, @OutputPath, @Status, @TargetId, @DateCreated, @Progress, @AdditionalFiles, @MediaSource, @IsMarkedForRemoval, @JobItemIndex)"; + _insertJobItemCommand.CommandText = "insert into SyncJobItems (Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource, IsMarkedForRemoval, JobItemIndex, ItemDateModifiedTicks) values (@Id, @ItemId, @ItemName, @MediaSourceId, @JobId, @TemporaryPath, @OutputPath, @Status, @TargetId, @DateCreated, @Progress, @AdditionalFiles, @MediaSource, @IsMarkedForRemoval, @JobItemIndex, @ItemDateModifiedTicks)"; _insertJobItemCommand.Parameters.Add(_insertJobItemCommand, "@Id"); _insertJobItemCommand.Parameters.Add(_insertJobItemCommand, "@ItemId"); @@ -144,10 +145,11 @@ namespace MediaBrowser.Server.Implementations.Sync _insertJobItemCommand.Parameters.Add(_insertJobItemCommand, "@MediaSource"); _insertJobItemCommand.Parameters.Add(_insertJobItemCommand, "@IsMarkedForRemoval"); _insertJobItemCommand.Parameters.Add(_insertJobItemCommand, "@JobItemIndex"); + _insertJobItemCommand.Parameters.Add(_insertJobItemCommand, "@ItemDateModifiedTicks"); // _updateJobItemCommand _updateJobItemCommand = _connection.CreateCommand(); - _updateJobItemCommand.CommandText = "update SyncJobItems set ItemId=@ItemId,ItemName=@ItemName,MediaSourceId=@MediaSourceId,JobId=@JobId,TemporaryPath=@TemporaryPath,OutputPath=@OutputPath,Status=@Status,TargetId=@TargetId,DateCreated=@DateCreated,Progress=@Progress,AdditionalFiles=@AdditionalFiles,MediaSource=@MediaSource,IsMarkedForRemoval=@IsMarkedForRemoval,JobItemIndex=@JobItemIndex where Id=@Id"; + _updateJobItemCommand.CommandText = "update SyncJobItems set ItemId=@ItemId,ItemName=@ItemName,MediaSourceId=@MediaSourceId,JobId=@JobId,TemporaryPath=@TemporaryPath,OutputPath=@OutputPath,Status=@Status,TargetId=@TargetId,DateCreated=@DateCreated,Progress=@Progress,AdditionalFiles=@AdditionalFiles,MediaSource=@MediaSource,IsMarkedForRemoval=@IsMarkedForRemoval,JobItemIndex=@JobItemIndex,ItemDateModifiedTicks=@ItemDateModifiedTicks where Id=@Id"; _updateJobItemCommand.Parameters.Add(_updateJobItemCommand, "@Id"); _updateJobItemCommand.Parameters.Add(_updateJobItemCommand, "@ItemId"); @@ -164,10 +166,11 @@ namespace MediaBrowser.Server.Implementations.Sync _updateJobItemCommand.Parameters.Add(_updateJobItemCommand, "@MediaSource"); _updateJobItemCommand.Parameters.Add(_updateJobItemCommand, "@IsMarkedForRemoval"); _updateJobItemCommand.Parameters.Add(_updateJobItemCommand, "@JobItemIndex"); + _updateJobItemCommand.Parameters.Add(_updateJobItemCommand, "@ItemDateModifiedTicks"); } private const string BaseJobSelectText = "select Id, TargetId, Name, Profile, Quality, Bitrate, Status, Progress, UserId, ItemIds, Category, ParentId, UnwatchedOnly, ItemLimit, SyncNewContent, DateCreated, DateLastModified, ItemCount from SyncJobs"; - private const string BaseJobItemSelectText = "select Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource, IsMarkedForRemoval, JobItemIndex from SyncJobItems"; + private const string BaseJobItemSelectText = "select Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource, IsMarkedForRemoval, JobItemIndex, ItemDateModifiedTicks from SyncJobItems"; public SyncJob GetJob(string id) { @@ -678,6 +681,7 @@ namespace MediaBrowser.Server.Implementations.Sync cmd.GetParameter(index++).Value = jobItem.MediaSource == null ? null : _json.SerializeToString(jobItem.MediaSource); cmd.GetParameter(index++).Value = jobItem.IsMarkedForRemoval; cmd.GetParameter(index++).Value = jobItem.JobItemIndex; + cmd.GetParameter(index++).Value = jobItem.ItemDateModifiedTicks; cmd.Transaction = transaction; @@ -782,6 +786,11 @@ namespace MediaBrowser.Server.Implementations.Sync info.IsMarkedForRemoval = reader.GetBoolean(13); info.JobItemIndex = reader.GetInt32(14); + if (!reader.IsDBNull(15)) + { + info.ItemDateModifiedTicks = reader.GetInt64(15); + } + return info; } diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 66aede029..dce839b57 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -8,5 +8,6 @@ <package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
<package id="morelinq" version="1.4.0" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
- <package id="SocketHttpListener" version="1.0.0.29" targetFramework="net45" />
+ <package id="SimpleInjector" version="3.1.3" targetFramework="net45" />
+ <package id="SocketHttpListener" version="1.0.0.30" targetFramework="net45" />
</packages>
\ No newline at end of file diff --git a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs index 0eab94a3b..fbfef9a34 100644 --- a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs +++ b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs @@ -69,6 +69,11 @@ namespace MediaBrowser.Server.Mono.Native } + public void AllowSystemStandby() + { + + } + public List<Assembly> GetAssembliesWithParts() { var list = new List<Assembly>(); diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 01575e71f..fd6dee0f6 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -515,7 +515,7 @@ namespace MediaBrowser.Server.Startup.Common UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); RegisterSingleInstance(UserViewManager); - var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager, UserViewManager); + var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager, UserViewManager, () => MediaEncoder); RegisterSingleInstance<IContentDirectory>(contentDirectory); var mediaRegistrar = new MediaReceiverRegistrar(LogManager.GetLogger("MediaReceiverRegistrar"), HttpClient, ServerConfigurationManager); diff --git a/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs b/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs index 20d4c6b2a..dbfd6f4e8 100644 --- a/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs +++ b/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs @@ -27,28 +27,27 @@ namespace MediaBrowser.Server.Startup.Common.EntryPoints _timer = new PeriodicTimer(obj => { var now = DateTime.UtcNow; - if (_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 15)) + var nativeApp = ((ApplicationHost)_appHost).NativeApp; + + try + { + if (_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 15)) + { + nativeApp.PreventSystemStandby(); + } + else + { + nativeApp.AllowSystemStandby(); + } + } + catch (Exception ex) { - KeepAlive(); + _logger.ErrorException("Error resetting system standby timer", ex); } }, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)); } - private void KeepAlive() - { - var nativeApp = ((ApplicationHost)_appHost).NativeApp; - - try - { - nativeApp.PreventSystemStandby(); - } - catch (Exception ex) - { - _logger.ErrorException("Error resetting system standby timer", ex); - } - } - public void Dispose() { if (_timer != null) diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs index 5ba5fb44a..0ae021407 100644 --- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs +++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs @@ -42,6 +42,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg catch { } + //_logger.Debug("ffmpeg decoder query result: {0}", output ?? string.Empty); var found = new List<string>(); var required = new[] @@ -78,6 +79,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg catch { } + //_logger.Debug("ffmpeg encoder query result: {0}", output ?? string.Empty); var found = new List<string>(); var required = new[] @@ -89,8 +91,8 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg //"libvpx", //"libvpx-vp9", "aac", - "ac3", "libmp3lame", + "libopus", //"libvorbis", "srt" }; diff --git a/MediaBrowser.Server.Startup.Common/INativeApp.cs b/MediaBrowser.Server.Startup.Common/INativeApp.cs index c603a908e..c0758b47f 100644 --- a/MediaBrowser.Server.Startup.Common/INativeApp.cs +++ b/MediaBrowser.Server.Startup.Common/INativeApp.cs @@ -93,6 +93,8 @@ namespace MediaBrowser.Server.Startup.Common /// </summary> void PreventSystemStandby(); + void AllowSystemStandby(); + /// <summary> /// Gets the power management. /// </summary> diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index cbf7d298c..dc61dcda8 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -273,11 +273,13 @@ namespace MediaBrowser.ServerApplication } private static ServerNotifyIcon _serverNotifyIcon; + private static TaskScheduler _mainTaskScheduler; private static void ShowTrayIcon() { //Application.EnableVisualStyles(); //Application.SetCompatibleTextRenderingDefault(false); _serverNotifyIcon = new ServerNotifyIcon(_appHost.LogManager, _appHost, _appHost.ServerConfigurationManager, _appHost.LocalizationManager); + _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); Application.Run(); } @@ -321,6 +323,18 @@ namespace MediaBrowser.ServerApplication } } + public static void Invoke(Action action) + { + if (_isRunningAsService) + { + action(); + } + else + { + Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, _mainTaskScheduler ?? TaskScheduler.Current); + } + } + /// <summary> /// Starts the service. /// </summary> @@ -555,9 +569,10 @@ namespace MediaBrowser.ServerApplication private static void ShutdownWindowsApplication() { - _logger.Info("Calling Application.Exit"); - Application.Exit(); + //_logger.Info("Calling Application.Exit"); + //Application.Exit(); + _logger.Info("Calling Environment.Exit"); Environment.Exit(0); _logger.Info("Calling ApplicationTaskCompletionSource.SetResult"); diff --git a/MediaBrowser.ServerApplication/Native/Standby.cs b/MediaBrowser.ServerApplication/Native/Standby.cs index 274c72b25..919709538 100644 --- a/MediaBrowser.ServerApplication/Native/Standby.cs +++ b/MediaBrowser.ServerApplication/Native/Standby.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; namespace MediaBrowser.ServerApplication.Native { @@ -7,11 +8,33 @@ namespace MediaBrowser.ServerApplication.Native /// </summary> public static class Standby { - public static void PreventSystemStandby() + public static void PreventSleepAndMonitorOff() { - SystemHelper.ResetStandbyTimer(); + NativeMethods.SetThreadExecutionState(NativeMethods.ES_CONTINUOUS | NativeMethods.ES_SYSTEM_REQUIRED | NativeMethods.ES_DISPLAY_REQUIRED); } + public static void PreventSleep() + { + NativeMethods.SetThreadExecutionState(NativeMethods.ES_CONTINUOUS | NativeMethods.ES_SYSTEM_REQUIRED); + } + + // Clear EXECUTION_STATE flags to allow the system to sleep and turn off monitor normally + public static void AllowSleep() + { + NativeMethods.SetThreadExecutionState(NativeMethods.ES_CONTINUOUS); + } + + internal static class NativeMethods + { + // Import SetThreadExecutionState Win32 API and necessary flags + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern uint SetThreadExecutionState(uint esFlags); + public const uint ES_CONTINUOUS = 0x80000000; + public const uint ES_SYSTEM_REQUIRED = 0x00000001; + public const uint ES_DISPLAY_REQUIRED = 0x00000002; + } + + [Flags] internal enum EXECUTION_STATE : uint { ES_NONE = 0, @@ -21,16 +44,5 @@ namespace MediaBrowser.ServerApplication.Native ES_AWAYMODE_REQUIRED = 0x00000040, ES_CONTINUOUS = 0x80000000 } - - public class SystemHelper - { - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags); - - public static void ResetStandbyTimer() - { - EXECUTION_STATE es = SetThreadExecutionState(EXECUTION_STATE.ES_SYSTEM_REQUIRED); - } - } } } diff --git a/MediaBrowser.ServerApplication/Native/WindowsApp.cs b/MediaBrowser.ServerApplication/Native/WindowsApp.cs index 34f4315de..10cd59436 100644 --- a/MediaBrowser.ServerApplication/Native/WindowsApp.cs +++ b/MediaBrowser.ServerApplication/Native/WindowsApp.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; +using System.Windows.Forms; using CommonIO; using MediaBrowser.Controller.Power; using MediaBrowser.Server.Startup.Common.FFMpeg; @@ -137,7 +138,12 @@ namespace MediaBrowser.ServerApplication.Native public void PreventSystemStandby() { - Standby.PreventSystemStandby(); + MainStartup.Invoke(Standby.PreventSleep); + } + + public void AllowSystemStandby() + { + MainStartup.Invoke(Standby.AllowSleep); } public IPowerManagement GetPowerManagement() diff --git a/MediaBrowser.ServerApplication/ServerNotifyIcon.cs b/MediaBrowser.ServerApplication/ServerNotifyIcon.cs index fbb93b660..27816db5a 100644 --- a/MediaBrowser.ServerApplication/ServerNotifyIcon.cs +++ b/MediaBrowser.ServerApplication/ServerNotifyIcon.cs @@ -36,10 +36,15 @@ namespace MediaBrowser.ServerApplication set { Action act = () => notifyIcon1.Visible = false; - contextMenuStrip1.Invoke(act); + Invoke(act); } } + public void Invoke(Action action) + { + contextMenuStrip1.Invoke(action); + } + public ServerNotifyIcon(ILogManager logManager, IServerApplicationHost appHost, IServerConfigurationManager configurationManager, diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 3a69c9666..376978790 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -346,7 +346,9 @@ namespace MediaBrowser.WebDashboard.Api } _fileSystem.DeleteDirectory(Path.Combine(bowerPath, "jquery", "src"), true); - + _fileSystem.DeleteDirectory(Path.Combine(bowerPath, "fingerprintjs2", "flash"), true); + _fileSystem.DeleteDirectory(Path.Combine(bowerPath, "fingerprintjs2", "specs"), true); + DeleteCryptoFiles(Path.Combine(bowerPath, "cryptojslib", "components")); DeleteFoldersByName(Path.Combine(bowerPath, "jquery"), "src"); diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index f7b550a13..6bc9f9b1b 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -101,6 +101,9 @@ <Content Include="dashboard-ui\autoorganizesmart.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\components\apphost.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\components\chromecasthelpers.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -110,9 +113,6 @@ <Content Include="dashboard-ui\components\favoriteitems.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\components\filedownloader.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\components\filterdialog\filterdialog.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -128,6 +128,12 @@ <Content Include="dashboard-ui\components\guestinviter\guestinviter.template.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\components\ironcardlist\ironcardlist.template.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\components\ironcardlist\ironcardlist.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\components\metadataeditor\metadataeditor.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -146,6 +152,9 @@ <Content Include="dashboard-ui\components\remotecontrolautoplay.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\components\scrollthreshold.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\components\servertestermessage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -221,12 +230,6 @@ <Content Include="dashboard-ui\components\playlisteditor\playlisteditor.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\components\tvguide\tvguide.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\components\tvguide\tvguide.template.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\devices\ie\ie.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -365,12 +368,6 @@ <Content Include="dashboard-ui\scripts\shared.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\sharingmanager.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\components\sharingwidget.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\supporterkeypage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -473,17 +470,6 @@ <Content Include="dashboard-ui\thirdparty\paper-button-style.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\.gitignore" />
- <Content Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\css\social-share-kit.css">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\fonts\social-share-kit.svg">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\js\social-share-kit.js" />
- <Content Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\js\social-share-kit.min.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\components\tvproviders\schedulesdirect.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -781,9 +767,6 @@ <Content Include="dashboard-ui\collections.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\css\images\editor\lock.png">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\css\images\editor\missing.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1712,6 +1695,9 @@ <None Include="dashboard-ui\strings\fr-CA.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
+ <None Include="dashboard-ui\strings\fr-FR.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
<None Include="dashboard-ui\strings\hu.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@@ -1730,17 +1716,6 @@ <None Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile-1.4.5.min.map">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\fonts\social-share-kit.eot">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\fonts\social-share-kit.ttf">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\fonts\social-share-kit.woff">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\LICENSE" />
- <None Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\README.md" />
<None Include="dashboard-ui\voice\grammar\en-US.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 75b5498c5..d020a73fe 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -662,7 +662,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers { if (!string.IsNullOrWhiteSpace(val)) { - val = val.Replace("plugin://plugin.video.youtube/?action=play_video&videoid=", "http://www.youtube.com/watch?v=", StringComparison.OrdinalIgnoreCase); + val = val.Replace("plugin://plugin.video.youtube/?action=play_video&videoid=", "https://www.youtube.com/watch?v=", StringComparison.OrdinalIgnoreCase); hasTrailer.AddTrailerUrl(val, false); } diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs index 5ce8d30ad..c1dd92987 100644 --- a/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs @@ -9,7 +9,7 @@ using CommonIO; namespace MediaBrowser.XbmcMetadata.Providers { - public abstract class BaseNfoProvider<T> : ILocalMetadataProvider<T>, IHasChangeMonitor + public abstract class BaseNfoProvider<T> : ILocalMetadataProvider<T>, IHasItemChangeMonitor where T : IHasMetadata, new() { protected IFileSystem FileSystem; @@ -57,7 +57,7 @@ namespace MediaBrowser.XbmcMetadata.Providers 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); @@ -66,7 +66,7 @@ namespace MediaBrowser.XbmcMetadata.Providers return false; } - return file.Exists && FileSystem.GetLastWriteTimeUtc(file) > date; + return file.Exists && FileSystem.GetLastWriteTimeUtc(file) > item.DateLastSaved; } public string Name diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index ba8a3bdf5..6d15d168d 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -479,7 +479,6 @@ namespace MediaBrowser.XbmcMetadata.Savers writer.WriteElementString("dateadded", item.DateCreated.ToLocalTime().ToString(DateAddedFormat)); writer.WriteElementString("title", item.Name ?? string.Empty); - writer.WriteElementString("originaltitle", item.Name ?? string.Empty); var hasOriginalTitle = item as IHasOriginalTitle; if (hasOriginalTitle != null) @@ -890,7 +889,7 @@ namespace MediaBrowser.XbmcMetadata.Savers { // This is what xbmc expects - return url.Replace("http://www.youtube.com/watch?v=", + return url.Replace("https://www.youtube.com/watch?v=", "plugin://plugin.video.youtube/?action=play_video&videoid=", StringComparison.OrdinalIgnoreCase); } diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 0ae08c4ad..a561dbf4b 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -13,8 +13,8 @@ <copyright>Copyright © Emby 2013</copyright> <dependencies> <dependency id="MediaBrowser.Common" version="3.0.645" /> - <dependency id="NLog" version="4.2.3" /> - <dependency id="SimpleInjector" version="3.1.2" /> + <dependency id="NLog" version="4.3.1" /> + <dependency id="SimpleInjector" version="3.1.3" /> </dependencies> </metadata> <files> diff --git a/OpenSubtitlesHandler/Utilities.cs b/OpenSubtitlesHandler/Utilities.cs index 2ae116521..d26c76b7c 100644 --- a/OpenSubtitlesHandler/Utilities.cs +++ b/OpenSubtitlesHandler/Utilities.cs @@ -32,7 +32,7 @@ namespace OpenSubtitlesHandler /// </summary> public sealed class Utilities { - private const string XML_RPC_SERVER = "http://api.opensubtitles.org/xml-rpc"; + private const string XML_RPC_SERVER = "https://api.opensubtitles.org/xml-rpc"; /// <summary> /// Compute movie hash @@ -195,7 +195,7 @@ namespace OpenSubtitlesHandler RequestContentBytes = request, RequestContentType = "text/xml", UserAgent = userAgent, - Host = "api.opensubtitles.org:80", + Host = "api.opensubtitles.org:443", Url = XML_RPC_SERVER, // Response parsing will fail with this enabled |
