diff --git a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs index b8300ac979..7779c2fc5d 100644 --- a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs +++ b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs @@ -155,6 +155,7 @@ namespace Emby.Drawing.ImageMagick AutoOrientImage(originalImage); } + AddForegroundLayer(originalImage, options); DrawIndicator(originalImage, width, height, options); originalImage.CurrentImage.CompressionQuality = quality; @@ -177,6 +178,8 @@ namespace Emby.Drawing.ImageMagick } wand.CurrentImage.CompositeImage(originalImage, CompositeOperator.OverCompositeOp, 0, 0); + + AddForegroundLayer(wand, options); DrawIndicator(wand, width, height, options); wand.CurrentImage.CompressionQuality = quality; @@ -189,6 +192,16 @@ namespace Emby.Drawing.ImageMagick SaveDelay(); } + private void AddForegroundLayer(MagickWand wand, ImageProcessingOptions options) + { + if (string.IsNullOrWhiteSpace(options.ForegroundLayer)) + { + return; + } + + // TODO + } + private void AutoOrientImage(MagickWand wand) { wand.CurrentImage.AutoOrientImage(); diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index e016127004..89e2649b53 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -234,7 +234,7 @@ namespace Emby.Drawing var quality = options.Quality; var outputFormat = GetOutputFormat(options.SupportedOutputFormats[0]); - var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor); + var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor, options.ForegroundLayer); var semaphore = GetLock(cacheFilePath); @@ -473,7 +473,7 @@ namespace Emby.Drawing /// /// Gets the cache file path based on a set of parameters /// - private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor) + private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor, string foregroundLayer) { var filename = originalPath; @@ -507,6 +507,11 @@ namespace Emby.Drawing filename += "b=" + backgroundColor; } + if (!string.IsNullOrEmpty(foregroundLayer)) + { + filename += "fl=" + foregroundLayer; + } + filename += "v=" + Version; return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLower()); diff --git a/MediaBrowser.Api/Images/ImageRequest.cs b/MediaBrowser.Api/Images/ImageRequest.cs index cdd348bb52..8b86ee7e0f 100644 --- a/MediaBrowser.Api/Images/ImageRequest.cs +++ b/MediaBrowser.Api/Images/ImageRequest.cs @@ -66,7 +66,10 @@ namespace MediaBrowser.Api.Images [ApiMember(Name = "BackgroundColor", Description = "Optional. Apply a background color for transparent images.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string BackgroundColor { get; set; } - + + [ApiMember(Name = "ForegroundLayer", Description = "Optional. Apply a foreground layer on top of the image.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ForegroundLayer { get; set; } + public ImageRequest() { EnableImageEnhancers = true; diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 7122c8fc12..8d58070fdf 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -624,6 +624,7 @@ namespace MediaBrowser.Api.Images PercentPlayed = request.PercentPlayed ?? 0, UnplayedCount = request.UnplayedCount, BackgroundColor = request.BackgroundColor, + ForegroundLayer = request.ForegroundLayer, SupportedOutputFormats = supportedFormats }; diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 00caa74d62..f66363da67 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -477,7 +477,7 @@ namespace MediaBrowser.Api.Playback var pts = string.Empty; - if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream) + if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && !state.VideoRequest.CopyTimestamps) { var seconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds; @@ -604,6 +604,10 @@ namespace MediaBrowser.Api.Playback { var seconds = Math.Round(TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds); + var setPtsParam = state.VideoRequest.CopyTimestamps + ? string.Empty + : string.Format(",setpts=PTS -{0}/TB", seconds.ToString(UsCulture)); + if (state.SubtitleStream.IsExternal) { var subtitlePath = state.SubtitleStream.Path; @@ -621,18 +625,18 @@ namespace MediaBrowser.Api.Playback } // TODO: Perhaps also use original_size=1920x800 ?? - return string.Format("subtitles=filename='{0}'{1},setpts=PTS -{2}/TB", + return string.Format("subtitles=filename='{0}'{1}{2}", MediaEncoder.EscapeSubtitleFilterPath(subtitlePath), charsetParam, - seconds.ToString(UsCulture)); + setPtsParam); } var mediaPath = state.MediaPath ?? string.Empty; - return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB", + return string.Format("subtitles='{0}:si={1}'{2}", MediaEncoder.EscapeSubtitleFilterPath(mediaPath), state.InternalSubtitleStreamOffset.ToString(UsCulture), - seconds.ToString(UsCulture)); + setPtsParam); } /// diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 50aa2df19c..0af419cf33 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -65,7 +65,8 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class VideoService : BaseProgressiveStreamingService { - public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, imageProcessor, httpClient) + public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) + : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, imageProcessor, httpClient) { } @@ -137,11 +138,6 @@ namespace MediaBrowser.Api.Playback.Progressive var isOutputMkv = string.Equals(state.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase); - if (state.RunTimeTicks.HasValue && state.VideoRequest.CopyTimestamps) - { - args += " -copyts -avoid_negative_ts disabled -start_at_zero"; - } - if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { if (state.VideoStream != null && IsH264(state.VideoStream) && @@ -160,10 +156,22 @@ namespace MediaBrowser.Api.Playback.Progressive var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; + var hasCopyTs = false; // Add resolution params, if specified if (!hasGraphicalSubs) { - args += GetOutputSizeParam(state, videoCodec); + var outputSizeParam = GetOutputSizeParam(state, videoCodec); + args += outputSizeParam; + hasCopyTs = outputSizeParam.IndexOf("copyts", StringComparison.OrdinalIgnoreCase) != -1; + } + + if (state.RunTimeTicks.HasValue && state.VideoRequest.CopyTimestamps) + { + if (!hasCopyTs) + { + args += " -copyts"; + } + args += " -avoid_negative_ts disabled -start_at_zero"; } var qualityParam = GetVideoQualityParam(state, videoCodec); diff --git a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs index ff11c889a6..1b5e260d71 100644 --- a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs +++ b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs @@ -20,7 +20,7 @@ namespace MediaBrowser.Common.Implementations.Networking Logger = logger; } - private volatile List _localIpAddresses; + private List _localIpAddresses; private readonly object _localIpAddressSyncLock = new object(); /// @@ -29,24 +29,20 @@ namespace MediaBrowser.Common.Implementations.Networking /// IPAddress. public IEnumerable GetLocalIpAddresses() { - const int cacheMinutes = 3; - var forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes; + const int cacheMinutes = 5; - if (_localIpAddresses == null || forceRefresh) + lock (_localIpAddressSyncLock) { - lock (_localIpAddressSyncLock) - { - forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes; + var forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes; - if (_localIpAddresses == null || forceRefresh) - { - var addresses = GetLocalIpAddressesInternal().ToList(); + if (_localIpAddresses == null || forceRefresh) + { + var addresses = GetLocalIpAddressesInternal().ToList(); - _localIpAddresses = addresses; - _lastRefresh = DateTime.UtcNow; + _localIpAddresses = addresses; + _lastRefresh = DateTime.UtcNow; - return addresses; - } + return addresses; } } diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index a4ccbb6f84..8d727a1128 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -103,7 +103,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks Logger = logger; _fileSystem = fileSystem; - ReloadTriggerEvents(true); + InitTriggerEvents(); } /// @@ -233,11 +233,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// /// The _triggers /// - private volatile List _triggers; - /// - /// The _triggers sync lock - /// - private readonly object _triggersSyncLock = new object(); + private List _triggers; /// /// Gets the triggers that define when the task will run /// @@ -247,17 +243,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks { get { - if (_triggers == null) - { - lock (_triggersSyncLock) - { - if (_triggers == null) - { - _triggers = LoadTriggers(); - } - } - } - return _triggers; } set @@ -303,6 +288,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks } } + private void InitTriggerEvents() + { + _triggers = LoadTriggers(); + ReloadTriggerEvents(true); + } + public void ReloadTriggerEvents() { ReloadTriggerEvents(false); diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index 2b80b701e0..3fd8d84dd5 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -39,6 +39,7 @@ namespace MediaBrowser.Controller.Drawing public double PercentPlayed { get; set; } public string BackgroundColor { get; set; } + public string ForegroundLayer { get; set; } public bool HasDefaultOptions(string originalImagePath) { @@ -83,7 +84,8 @@ namespace MediaBrowser.Controller.Drawing !AddPlayedIndicator && PercentPlayed.Equals(0) && !UnplayedCount.HasValue && - string.IsNullOrEmpty(BackgroundColor); + string.IsNullOrEmpty(BackgroundColor) && + string.IsNullOrEmpty(ForegroundLayer); } private bool IsFormatSupported(string originalImagePath) diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index 095fa95e10..50c41fd217 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -31,6 +31,7 @@ namespace MediaBrowser.Model.LiveTv public string Type { get; set; } public bool ImportFavoritesOnly { get; set; } public bool IsEnabled { get; set; } + public string GuideGroup { get; set; } public TunerHostInfo() { @@ -48,5 +49,6 @@ namespace MediaBrowser.Model.LiveTv public string ZipCode { get; set; } public string Country { get; set; } public string Path { get; set; } + public string GuideGroup { get; set; } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs index 51642cf5dc..2d3c203a23 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs @@ -135,7 +135,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization if (previousResult != null) { // Don't keep saving the same result over and over if nothing has changed - if (previousResult.Status == result.Status && result.Status != FileSortingStatus.Success) + if (previousResult.Status == result.Status && previousResult.StatusMessage == result.StatusMessage && result.Status != FileSortingStatus.Success) { return previousResult; } diff --git a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs index 2161c14547..884e1e3225 100644 --- a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs +++ b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs @@ -125,10 +125,21 @@ namespace MediaBrowser.Server.Implementations.Library.Validators validIds.Add(item.Id); + var hasMetdata = !string.IsNullOrWhiteSpace(item.Overview); + var performFullRefresh = !hasMetdata && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= 30; + + var defaultMetadataRefreshMode = performFullRefresh + ? MetadataRefreshMode.FullRefresh + : MetadataRefreshMode.Default; + + var imageRefreshMode = performFullRefresh + ? ImageRefreshMode.FullRefresh + : ImageRefreshMode.Default; + var options = new MetadataRefreshOptions(_fileSystem) { - MetadataRefreshMode = person.Value ? MetadataRefreshMode.Default : MetadataRefreshMode.ValidationOnly, - ImageRefreshMode = person.Value ? ImageRefreshMode.Default : ImageRefreshMode.ValidationOnly + MetadataRefreshMode = person.Value ? defaultMetadataRefreshMode : MetadataRefreshMode.ValidationOnly, + ImageRefreshMode = person.Value ? imageRefreshMode : ImageRefreshMode.ValidationOnly }; await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index b29a7562cf..68b3f1f71e 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV where T : class { private readonly object _fileDataLock = new object(); - private volatile List _items; + private List _items; private readonly IJsonSerializer _jsonSerializer; protected readonly ILogger Logger; private readonly string _dataPath; @@ -31,17 +31,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public IReadOnlyList GetAll() { - if (_items == null) + lock (_fileDataLock) { - lock (_fileDataLock) + if (_items == null) { - if (_items == null) - { - _items = GetItemsFromFile(_dataPath); - } + _items = GetItemsFromFile(_dataPath); } + return _items; } - return _items; } private List GetItemsFromFile(string path) diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index d6378d2b76..fb99e5f9db 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -466,12 +466,6 @@ PreserveNewest - - PreserveNewest - - - PreserveNewest - PreserveNewest @@ -550,9 +544,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -1402,6 +1393,42 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -2232,6 +2259,15 @@ + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + diff --git a/MediaBrowser.sln b/MediaBrowser.sln index a9b5020ec2..a55ae200a8 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F0E0E64C-2A6F-4E35-9533-D53AC07C2CD1}" EndProject @@ -521,7 +521,4 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection EndGlobal