diff --git a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs index d0f095f5b5..0801a371b5 100644 --- a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs +++ b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs @@ -5,6 +5,7 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Querying; @@ -36,6 +37,15 @@ namespace MediaBrowser.Api.DefaultTheme [ApiMember(Name = "TopCommunityRating", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public double TopCommunityRating { get; set; } + + [ApiMember(Name = "NextUpEpisodeLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int NextUpEpisodeLimit { get; set; } + + [ApiMember(Name = "ResumableEpisodeLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int ResumableEpisodeLimit { get; set; } + + [ApiMember(Name = "LatestEpisodeLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int LatestEpisodeLimit { get; set; } } [Route("/MBT/DefaultTheme/Movies", "GET")] @@ -70,8 +80,9 @@ namespace MediaBrowser.Api.DefaultTheme private readonly IUserDataManager _userDataManager; private readonly IImageProcessor _imageProcessor; + private readonly IItemRepository _itemRepo; - public DefaultThemeService(IUserManager userManager, IDtoService dtoService, ILogger logger, ILibraryManager libraryManager, IImageProcessor imageProcessor, IUserDataManager userDataManager) + public DefaultThemeService(IUserManager userManager, IDtoService dtoService, ILogger logger, ILibraryManager libraryManager, IImageProcessor imageProcessor, IUserDataManager userDataManager, IItemRepository itemRepo) { _userManager = userManager; _dtoService = dtoService; @@ -79,6 +90,7 @@ namespace MediaBrowser.Api.DefaultTheme _libraryManager = libraryManager; _imageProcessor = imageProcessor; _userDataManager = userDataManager; + _itemRepo = itemRepo; } public object Get(GetFavoritesView request) @@ -114,6 +126,8 @@ namespace MediaBrowser.Api.DefaultTheme .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) .ToList(); + fields.Add(ItemFields.PrimaryImageAspectRatio); + view.Albums = itemsWithImages .OfType() .OrderBy(i => Guid.NewGuid()) @@ -255,6 +269,9 @@ namespace MediaBrowser.Api.DefaultTheme public object Get(GetTvView request) { + var romanceGenres = request.RomanceGenre.Split(',').ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); + var comedyGenres = request.ComedyGenre.Split(',').ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); + var user = _userManager.GetUserById(request.UserId); var series = user.RootFolder.GetRecursiveChildren(user) @@ -269,9 +286,16 @@ namespace MediaBrowser.Api.DefaultTheme FavoriteSeriesCount = series.Count(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite), - TopCommunityRatedSeriesCount = series.Count(i => i.CommunityRating.HasValue && i.CommunityRating.Value >= request.TopCommunityRating) + TopCommunityRatedSeriesCount = series.Count(i => i.CommunityRating.HasValue && i.CommunityRating.Value >= request.TopCommunityRating), + + ComedySeriesCount = series.Count(i => i.Genres.Any(comedyGenres.ContainsKey)), + + RomanticSeriesCount = series.Count(i => i.Genres.Any(romanceGenres.ContainsKey)) }; + SetFavoriteGenres(view, series, user); + SetFavoriteStudios(view, series, user); + var fields = new List(); var seriesWithBestBackdrops = FilterItemsForBackdropDisplay(seriesWithBackdrops).ToList(); @@ -291,9 +315,6 @@ namespace MediaBrowser.Api.DefaultTheme .Take(1) .ToList(); - var romanceGenres = request.RomanceGenre.Split(',').ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); - var comedyGenres = request.ComedyGenre.Split(',').ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); - view.RomanceItems = seriesWithBackdrops .Where(i => i.Genres.Any(romanceGenres.ContainsKey)) .OrderBy(i => Guid.NewGuid()) @@ -347,9 +368,114 @@ namespace MediaBrowser.Api.DefaultTheme .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) .ToList(); + var nextUpEpisodes = new TvShowsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService) + .GetNextUpEpisodes(new GetNextUpEpisodes { UserId = user.Id }, series) + .ToList(); + + fields.Add(ItemFields.PrimaryImageAspectRatio); + + view.NextUpEpisodes = nextUpEpisodes + .Take(request.NextUpEpisodeLimit) + .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) + .ToList(); + + view.SeriesIdsInProgress = nextUpEpisodes.Select(i => i.Series.Id.ToString("N")).ToList(); + + var ownedEpisodes = series + .SelectMany(i => i.GetRecursiveChildren(user, j => j.LocationType != LocationType.Virtual)) + .OfType() + .ToList(); + + // Avoid implicitly captured closure + var currentUser = user; + + view.LatestEpisodes = ownedEpisodes + .OrderByDescending(i => i.DateCreated) + .Where(i => !_userDataManager.GetUserData(currentUser.Id, i.GetUserDataKey()).Played) + .Take(request.LatestEpisodeLimit) + .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) + .ToList(); + + view.ResumableEpisodes = ownedEpisodes + .Where(i => _userDataManager.GetUserData(currentUser.Id, i.GetUserDataKey()).PlaybackPositionTicks > 0) + .OrderByDescending(i => _userDataManager.GetUserData(currentUser.Id, i.GetUserDataKey()).LastPlayedDate ?? DateTime.MinValue) + .Take(request.ResumableEpisodeLimit) + .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) + .ToList(); + return ToOptimizedResult(view); } + private void SetFavoriteGenres(TvView view, IEnumerable inputItems, User user) + { + var all = inputItems.SelectMany(i => i.Genres) + .Distinct(StringComparer.OrdinalIgnoreCase); + + view.FavoriteGenres = all.Select(i => + { + try + { + var itemByName = _libraryManager.GetGenre(i); + + var counts = itemByName.GetItemByNameCounts(user); + + var count = counts == null ? 0 : counts.SeriesCount; + + if (count > 0 && _userDataManager.GetUserData(user.Id, itemByName.GetUserDataKey()).IsFavorite) + { + return new ItemByNameInfo + { + Name = itemByName.Name, + ItemCount = count + }; + } + } + catch (Exception ex) + { + _logger.ErrorException("Error getting genre {0}", ex, i); + + } + + return null; + + }).Where(i => i != null).ToList(); + } + + private void SetFavoriteStudios(TvView view, IEnumerable inputItems, User user) + { + var all = inputItems.SelectMany(i => i.Studios) + .Distinct(StringComparer.OrdinalIgnoreCase); + + view.FavoriteStudios = all.Select(i => + { + try + { + var itemByName = _libraryManager.GetStudio(i); + + var counts = itemByName.GetItemByNameCounts(user); + + var count = counts == null ? 0 : counts.SeriesCount; + + if (count > 0 && _userDataManager.GetUserData(user.Id, itemByName.GetUserDataKey()).IsFavorite) + { + return new ItemByNameInfo + { + Name = itemByName.Name, + ItemCount = count + }; + } + } + catch (Exception ex) + { + _logger.ErrorException("Error getting studio {0}", ex, i); + + } + + return null; + + }).Where(i => i != null).ToList(); + } + public object Get(GetMovieView request) { var user = _userManager.GetUserById(request.UserId); diff --git a/MediaBrowser.Api/DefaultTheme/Models.cs b/MediaBrowser.Api/DefaultTheme/Models.cs index 70f95a90f1..af05eecb2b 100644 --- a/MediaBrowser.Api/DefaultTheme/Models.cs +++ b/MediaBrowser.Api/DefaultTheme/Models.cs @@ -44,7 +44,22 @@ namespace MediaBrowser.Api.DefaultTheme public int SeriesCount { get; set; } public int FavoriteSeriesCount { get; set; } public int TopCommunityRatedSeriesCount { get; set; } - public int InProgressSeriesCount { get; set; } + public int ComedySeriesCount { get; set; } + public int RomanticSeriesCount { get; set; } + + public List FavoriteGenres { get; set; } + public List FavoriteStudios { get; set; } + public List SeriesIdsInProgress { get; set; } + + public List LatestEpisodes { get; set; } + public List NextUpEpisodes { get; set; } + public List ResumableEpisodes { get; set; } + } + + public class ItemByNameInfo + { + public string Name { get; set; } + public int ItemCount { get; set; } } public class GamesView : BaseView diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 0ac181dea2..6cae379d2a 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -96,7 +96,7 @@ namespace MediaBrowser.Api /// /// The _user data repository /// - private readonly IUserDataManager _userDataRepository; + private readonly IUserDataManager _userDataManager; /// /// The _library manager /// @@ -109,12 +109,12 @@ namespace MediaBrowser.Api /// Initializes a new instance of the class. /// /// The user manager. - /// The user data repository. + /// The user data repository. /// The library manager. - public TvShowsService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService) + public TvShowsService(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService) { _userManager = userManager; - _userDataRepository = userDataRepository; + _userDataManager = userDataManager; _libraryManager = libraryManager; _itemRepo = itemRepo; _dtoService = dtoService; @@ -130,7 +130,7 @@ namespace MediaBrowser.Api var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager, _itemRepo, _libraryManager, - _userDataRepository, + _userDataManager, _dtoService, Logger, request, item => item is Series, @@ -146,7 +146,7 @@ namespace MediaBrowser.Api /// System.Object. public object Get(GetNextUpEpisodes request) { - var result = GetNextUpEpisodes(request); + var result = GetNextUpEpisodeItemsResult(request); return ToOptimizedResult(result); } @@ -156,7 +156,27 @@ namespace MediaBrowser.Api /// /// The request. /// Task{ItemsResult}. - private ItemsResult GetNextUpEpisodes(GetNextUpEpisodes request) + private ItemsResult GetNextUpEpisodeItemsResult(GetNextUpEpisodes request) + { + var user = _userManager.GetUserById(request.UserId); + + var itemsList = GetNextUpEpisodes(request) + .ToList(); + + var pagedItems = ApplyPaging(request, itemsList); + + var fields = request.GetItemFields().ToList(); + + var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray(); + + return new ItemsResult + { + TotalRecordCount = itemsList.Count, + Items = returnItems + }; + } + + public IEnumerable GetNextUpEpisodes(GetNextUpEpisodes request) { var user = _userManager.GetUserById(request.UserId); @@ -164,19 +184,24 @@ namespace MediaBrowser.Api .GetRecursiveChildren(user) .OfType(); - items = FilterSeries(request, items); + // Avoid implicitly captured closure + return GetNextUpEpisodes(request, items); + } - var itemsList = items + public IEnumerable GetNextUpEpisodes(GetNextUpEpisodes request, IEnumerable series) + { + var user = _userManager.GetUserById(request.UserId); + + // Avoid implicitly captured closure + var currentUser = user; + + return FilterSeries(request, series) .AsParallel() - .Select(i => GetNextUp(i, user, request)) - .ToList(); - - itemsList = itemsList - .Where(i => i.Item1 != null) + .Select(i => GetNextUp(i, currentUser, request).Item1) + .Where(i => i != null) .OrderByDescending(i => { - var seriesUserData = - _userDataRepository.GetUserData(user.Id, i.Item1.Series.GetUserDataKey()); + var seriesUserData = _userDataManager.GetUserData(user.Id, i.Series.GetUserDataKey()); if (seriesUserData.IsFavorite) { @@ -190,20 +215,7 @@ namespace MediaBrowser.Api return 0; }) - .ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue) - .ToList(); - - var pagedItems = ApplyPaging(request, itemsList.Select(i => i.Item1)); - - var fields = request.GetItemFields().ToList(); - - var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray(); - - return new ItemsResult - { - TotalRecordCount = itemsList.Count, - Items = returnItems - }; + .ThenByDescending(i => i.PremiereDate ?? DateTime.MinValue); } /// @@ -230,7 +242,7 @@ namespace MediaBrowser.Api // Go back starting with the most recent episodes foreach (var episode in allEpisodes) { - var userData = _userDataRepository.GetUserData(user.Id, episode.GetUserDataKey()); + var userData = _userDataManager.GetUserData(user.Id, episode.GetUserDataKey()); if (userData.Played) { diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 0ce3d770ee..d0eff49fad 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -227,12 +227,6 @@ namespace MediaBrowser.Model.Configuration /// /// The width of the min series backdrop. public int MinSeriesBackdropWidth { get; set; } - - /// - /// Gets or sets the width of the min movie poster. - /// - /// The width of the min movie poster. - public int MinMoviePosterWidth { get; set; } /// /// Gets or sets a value indicating whether [enable people prefix sub folders]. @@ -291,7 +285,6 @@ namespace MediaBrowser.Model.Configuration MinMovieBackdropWidth = 1920; MinSeriesBackdropWidth = 1920; - MinMoviePosterWidth = 1000; } } diff --git a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs index 6c503ad3ac..416ebbc1b5 100644 --- a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs @@ -191,7 +191,7 @@ namespace MediaBrowser.Providers.Movies cancellationToken.ThrowIfCancellationRequested(); var eligiblePosters = images - .Where(i => i.Type == ImageType.Primary && i.Width.HasValue && i.Width.Value >= ConfigurationManager.Configuration.MinMoviePosterWidth) + .Where(i => i.Type == ImageType.Primary) .ToList(); // poster diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index eb9c920d76..9d71fef0ad 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -536,9 +536,12 @@ namespace MediaBrowser.Providers.Movies { dataFilePath = GetDataFilePath(item); - var mainResult = JsonSerializer.DeserializeFromFile(dataFilePath); + if (!string.IsNullOrEmpty(dataFilePath)) + { + var mainResult = JsonSerializer.DeserializeFromFile(dataFilePath); - ProcessMainInfo(item, mainResult); + ProcessMainInfo(item, mainResult); + } } } diff --git a/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs b/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs index 748ba0df47..a9f83dfffd 100644 --- a/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs +++ b/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs @@ -329,7 +329,7 @@ namespace MediaBrowser.Server.Implementations.IO } catch (IOException ex) { - Logger.ErrorException("IOException in watcher changed", ex); + Logger.ErrorException("IOException in watcher changed. Path: {0}", ex, e.FullPath); } }