diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index ab20ab730..7e352e4de 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -542,7 +542,7 @@ namespace MediaBrowser.Api.Playback var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture); return isH264Output ? - string.Format("{3} -vf \"{0}scale=min(iw\\,{1}):trunc(ow/a/2)*2{2}\"", yadifParam, maxWidthParam, assSubtitleParam, copyTsParam) : + string.Format("{3} -vf \"{0}scale=min(iw\\,{1}):trunc(ow/dar/2)*2{2}\"", yadifParam, maxWidthParam, assSubtitleParam, copyTsParam) : string.Format("{3} -vf \"{0}scale=min(iw\\,{1}):-1{2}\"", yadifParam, maxWidthParam, assSubtitleParam, copyTsParam); } diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs index 8366e6fb9..083ec8c82 100644 --- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs +++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs @@ -147,7 +147,7 @@ namespace MediaBrowser.Dlna.Ssdp SendDatagram(header, values, endpoint, null); - _logger.Info("{1} - Responded to a {0} request to {2}", d.Type, endpoint, d.Address.ToString()); + _logger.Debug("{1} - Responded to a {0} request to {2}", d.Type, endpoint, d.Address.ToString()); } } } diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index 7e21e1ef2..5a6be389d 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -1,8 +1,6 @@ -using System; +using MediaBrowser.Model.MediaInfo; using System.Collections.Generic; using System.Linq; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Model.Dlna { diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index dc3a04769..662bbdf3e 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -11,6 +11,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; using System.IO; @@ -33,8 +34,9 @@ namespace MediaBrowser.Server.Implementations.Channels private readonly ILogger _logger; private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; + private readonly IJsonSerializer _jsonSerializer; - public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager) + public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager, IJsonSerializer jsonSerializer) { _userManager = userManager; _dtoService = dtoService; @@ -43,6 +45,7 @@ namespace MediaBrowser.Server.Implementations.Channels _config = config; _fileSystem = fileSystem; _userDataManager = userDataManager; + _jsonSerializer = jsonSerializer; } public void AddParts(IEnumerable channels, IEnumerable factories) @@ -227,19 +230,90 @@ namespace MediaBrowser.Server.Implementations.Channels return await GetReturnItems(items, user, query, cancellationToken).ConfigureAwait(false); } + private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1); private async Task> GetChannelItems(IChannel channel, User user, string categoryId, CancellationToken cancellationToken) { - // TODO: Put some caching in here + var cachePath = GetChannelDataCachePath(channel, user, categoryId); - var query = new InternalChannelItemQuery + try { - User = user, - CategoryId = categoryId - }; + var channelItemResult = _jsonSerializer.DeserializeFromFile(cachePath); - var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false); + if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(channelItemResult.CacheLength) > DateTime.UtcNow) + { + return channelItemResult.Items; + } + } + catch (FileNotFoundException) + { - return result.Items; + } + catch (DirectoryNotFoundException) + { + + } + + await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + try + { + var channelItemResult = _jsonSerializer.DeserializeFromFile(cachePath); + + if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(channelItemResult.CacheLength) > DateTime.UtcNow) + { + return channelItemResult.Items; + } + } + catch (FileNotFoundException) + { + + } + catch (DirectoryNotFoundException) + { + + } + + var query = new InternalChannelItemQuery + { + User = user, + CategoryId = categoryId + }; + + var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false); + + CacheResponse(result, cachePath); + + return result.Items; + } + finally + { + _resourcePool.Release(); + } + } + + private void CacheResponse(ChannelItemResult result, string path) + { + try + { + Directory.CreateDirectory(Path.GetDirectoryName(path)); + + _jsonSerializer.SerializeToFile(result, path); + } + catch (Exception ex) + { + _logger.ErrorException("Error writing to channel cache file: {0}", ex, path); + } + } + + private string GetChannelDataCachePath(IChannel channel, User user, string categoryId) + { + var channelId = GetInternalChannelId(channel.Name).ToString("N"); + + var categoryKey = string.IsNullOrWhiteSpace(categoryId) ? "root" : categoryId.GetMD5().ToString("N"); + + return Path.Combine(_config.ApplicationPaths.CachePath, channelId, categoryKey, user.Id.ToString("N") + ".json"); } private async Task> GetReturnItems(IEnumerable items, User user, ChannelItemQuery query, CancellationToken cancellationToken) diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs index cc3221711..43e968fa5 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs @@ -164,6 +164,12 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications var item = e.MediaInfo; + if (item == null) + { + _logger.Warn("PlaybackStart reported with null media info."); + return; + } + if (e.Item != null && e.Item.Parent == null) { // Don't report theme song or local trailer playback diff --git a/MediaBrowser.Server.Implementations/Library/Validators/BoxSetPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/BoxSetPostScanTask.cs index f02c907c6..86d88f7e0 100644 --- a/MediaBrowser.Server.Implementations/Library/Validators/BoxSetPostScanTask.cs +++ b/MediaBrowser.Server.Implementations/Library/Validators/BoxSetPostScanTask.cs @@ -27,7 +27,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators foreach (var boxset in boxsets) { - foreach (var child in boxset.GetLinkedChildren().OfType()) + foreach (var child in boxset.Children.Concat(boxset.GetLinkedChildren()).OfType()) { var boxsetIdList = child.BoxSetIdList.ToList(); if (!boxsetIdList.Contains(boxset.Id)) diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 555899f3b..befcd701e 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -507,7 +507,7 @@ namespace MediaBrowser.ServerApplication MediaEncoder); RegisterSingleInstance(EncodingManager); - ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, Logger, ServerConfigurationManager, FileSystemManager, UserDataManager); + ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, Logger, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer); RegisterSingleInstance(ChannelManager); var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger);