From 9f9ab1ac9f0d37e881d1107483a4772ad503172f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 17 Nov 2013 10:27:48 -0500 Subject: [PATCH] support metabrowser special episode attributes --- .../DefaultTheme/DefaultThemeService.cs | 102 ++++-------------- MediaBrowser.Api/DefaultTheme/Models.cs | 5 +- MediaBrowser.Api/UserLibrary/ItemsService.cs | 4 +- MediaBrowser.Controller/Entities/BaseItem.cs | 6 +- MediaBrowser.Controller/Entities/Folder.cs | 5 +- .../Entities/TV/Episode.cs | 4 +- .../Savers/EpisodeXmlSaver.cs | 18 +++- MediaBrowser.Providers/TV/EpisodeXmlParser.cs | 53 +++++++-- .../TV/TvdbEpisodeProvider.cs | 26 ++++- .../Dto/DtoService.cs | 2 +- .../MediaEncoder/MediaEncoder.cs | 5 +- 11 files changed, 119 insertions(+), 111 deletions(-) diff --git a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs index 379698059f..6c1aa04cfb 100644 --- a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs +++ b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs @@ -21,6 +21,9 @@ namespace MediaBrowser.Api.DefaultTheme { [ApiMember(Name = "UserId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] public Guid UserId { get; set; } + + [ApiMember(Name = "RecentlyPlayedGamesLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int RecentlyPlayedGamesLimit { get; set; } } [Route("/MBT/DefaultTheme/TV", "GET")] @@ -245,6 +248,19 @@ namespace MediaBrowser.Api.DefaultTheme var fields = new List(); + view.GameSystems = items + .OfType() + .OrderBy(i => i.SortName) + .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) + .ToList(); + + var currentUserId = user.Id; + view.RecentlyPlayedGames = gamesWithImages + .OrderByDescending(i => _userDataManager.GetUserData(currentUserId, i.GetUserDataKey()).LastPlayedDate ?? DateTime.MinValue) + .Take(request.RecentlyPlayedGamesLimit) + .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) + .ToList(); + view.BackdropItems = gamesWithBackdrops .OrderBy(i => Guid.NewGuid()) .Take(10) @@ -265,12 +281,6 @@ namespace MediaBrowser.Api.DefaultTheme .Take(1) .ToList(); - view.MiniSpotlights = gamesWithBackdrops - .Randomize("minispotlight") - .Take(5) - .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) - .ToList(); - return ToOptimizedResult(view); } @@ -324,8 +334,6 @@ namespace MediaBrowser.Api.DefaultTheme .Take(1) .ToList(); - view.ActorItems = GetActors(series, user.Id); - var spotlightSeries = seriesWithBestBackdrops .Where(i => i.CommunityRating.HasValue && i.CommunityRating >= 8.5) .ToList(); @@ -409,10 +417,6 @@ namespace MediaBrowser.Api.DefaultTheme var items = user.RootFolder.GetRecursiveChildren(user, i => i is Movie || i is Trailer || i is BoxSet) .ToList(); - // Exclude trailers from backdrops because they're not always 1080p - var itemsWithBackdrops = items.Where(i => i.BackdropImagePaths.Count > 0) - .ToList(); - var view = new MoviesView(); var movies = items.OfType() @@ -439,7 +443,7 @@ namespace MediaBrowser.Api.DefaultTheme var fields = new List(); - var itemsWithTopBackdrops = FilterItemsForBackdropDisplay(itemsWithBackdrops).ToList(); + var itemsWithTopBackdrops = FilterItemsForBackdropDisplay(moviesWithBackdrops).ToList(); view.BackdropItems = itemsWithTopBackdrops .OrderBy(i => Guid.NewGuid()) @@ -515,10 +519,10 @@ namespace MediaBrowser.Api.DefaultTheme .Take(1) .ToList(); - view.PeopleItems = GetActors(items, user.Id); - + var currentUserId = user.Id; var spotlightItems = itemsWithTopBackdrops .Where(i => i.CommunityRating.HasValue && i.CommunityRating >= 8) + .Where(i => !_userDataManager.GetUserData(currentUserId, i.GetUserDataKey()).Played) .ToList(); if (spotlightItems.Count < 20) @@ -551,14 +555,13 @@ namespace MediaBrowser.Api.DefaultTheme .ToList(); // Avoid implicitly captured closure - var currentUserId = user.Id; - miniSpotlightItems.InsertRange(2, moviesWithBackdrops + miniSpotlightItems.InsertRange(0, moviesWithBackdrops .Where(i => _userDataManager.GetUserData(currentUserId, i.GetUserDataKey()).PlaybackPositionTicks > 0) .OrderByDescending(i => _userDataManager.GetUserData(currentUserId, i.GetUserDataKey()).LastPlayedDate ?? DateTime.MaxValue) .Take(3)); view.MiniSpotlights = miniSpotlightItems - .Take(5) + .Take(3) .Select(i => _dtoService.GetBaseItemDto(i, fields, user)) .ToList(); @@ -617,69 +620,6 @@ namespace MediaBrowser.Api.DefaultTheme } } - private List GetActors(IEnumerable mediaItems, Guid userId) - { - var actors = mediaItems.SelectMany(i => i.People) - .Select(i => i.Name) - .Distinct(StringComparer.OrdinalIgnoreCase) - .Randomize() - .ToList(); - - var result = actors.Select(actor => - { - try - { - var person = _libraryManager.GetPerson(actor); - - if (!string.IsNullOrEmpty(person.PrimaryImagePath)) - { - var userdata = _userDataManager.GetUserData(userId, person.GetUserDataKey()); - - if (userdata.IsFavorite || (userdata.Likes ?? false)) - { - return GetItemStub(person, ImageType.Primary); - } - } - } - catch (Exception ex) - { - _logger.ErrorException("Error getting person {0}", ex, actor); - } - - return null; - }) - .Where(i => i != null) - .Take(1) - .ToList(); - - if (result.Count == 0) - { - result = actors.Select(actor => - { - try - { - var person = _libraryManager.GetPerson(actor); - - if (!string.IsNullOrEmpty(person.PrimaryImagePath)) - { - return GetItemStub(person, ImageType.Primary); - } - } - catch (Exception ex) - { - _logger.ErrorException("Error getting person {0}", ex, actor); - } - - return null; - }) - .Where(i => i != null) - .Take(1) - .ToList(); - } - - return result; - } - private ItemStub GetItemStub(BaseItem item, ImageType imageType) { var stub = new ItemStub diff --git a/MediaBrowser.Api/DefaultTheme/Models.cs b/MediaBrowser.Api/DefaultTheme/Models.cs index 5219c44f91..f261a9aff7 100644 --- a/MediaBrowser.Api/DefaultTheme/Models.cs +++ b/MediaBrowser.Api/DefaultTheme/Models.cs @@ -16,7 +16,6 @@ namespace MediaBrowser.Api.DefaultTheme public class MoviesView : BaseView { public List MovieItems { get; set; } - public List PeopleItems { get; set; } public List BoxSetItems { get; set; } public List TrailerItems { get; set; } @@ -39,7 +38,6 @@ namespace MediaBrowser.Api.DefaultTheme public class TvView : BaseView { public List ShowsItems { get; set; } - public List ActorItems { get; set; } public List RomanceItems { get; set; } public List ComedyItems { get; set; } @@ -60,7 +58,8 @@ namespace MediaBrowser.Api.DefaultTheme public class GamesView : BaseView { public List MultiPlayerItems { get; set; } - + public List GameSystems { get; set; } + public List RecentlyPlayedGames { get; set; } } public class BaseView diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 3c6ea62f47..1c7722e587 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -1013,9 +1013,9 @@ namespace MediaBrowser.Api.UserLibrary if (episode != null) { - var seasonNumber = episode.SpecialSeasonNumber ?? episode.ParentIndexNumber; + var seasonNumber = episode.AirsAfterSeasonNumber ?? episode.AirsBeforeEpisodeNumber ?? episode.ParentIndexNumber; - return seasonNumber.HasValue && seasonNumber.Value == val; + return episode.PremiereDate.HasValue && seasonNumber.HasValue && seasonNumber.Value == val; } return false; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 9cd839b21a..a6178536c6 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1728,11 +1728,9 @@ namespace MediaBrowser.Controller.Entities // If we didn't the metafile entry, check the Season if (metaFileEntry == null) { - var episode = this as Episode; - - if (episode != null && episode.Season != null) + if (Parent != null) { - episode.Season.ResolveArgs.GetMetaFileByPath(imagePath); + metaFileEntry = Parent.ResolveArgs.GetMetaFileByPath(imagePath); } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index f92e7e3e05..8dbc981938 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1,7 +1,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Resolvers; @@ -1315,7 +1314,7 @@ namespace MediaBrowser.Controller.Entities public override async Task MarkPlayed(User user, DateTime? datePlayed, IUserDataManager userManager) { // Sweep through recursively and update status - var tasks = GetRecursiveChildren(user, true).Where(i => !i.IsFolder).Select(c => c.MarkPlayed(user, datePlayed, userManager)); + var tasks = GetRecursiveChildren(user, true).Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual).Select(c => c.MarkPlayed(user, datePlayed, userManager)); await Task.WhenAll(tasks).ConfigureAwait(false); } @@ -1329,7 +1328,7 @@ namespace MediaBrowser.Controller.Entities public override async Task MarkUnplayed(User user, IUserDataManager userManager) { // Sweep through recursively and update status - var tasks = GetRecursiveChildren(user, true).Where(i => !i.IsFolder).Select(c => c.MarkUnplayed(user, userManager)); + var tasks = GetRecursiveChildren(user, true).Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual).Select(c => c.MarkUnplayed(user, userManager)); await Task.WhenAll(tasks).ConfigureAwait(false); } diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 5100916ffa..c68ba0ad1f 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -36,7 +36,9 @@ namespace MediaBrowser.Controller.Entities.TV /// Gets the season in which it aired. /// /// The aired season. - public int? SpecialSeasonNumber { get; set; } + public int? AirsBeforeSeasonNumber { get; set; } + public int? AirsAfterSeasonNumber { get; set; } + public int? AirsBeforeEpisodeNumber { get; set; } /// /// We want to group into series not show individually in an index diff --git a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs b/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs index 6d7a607565..1c37b07894 100644 --- a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs @@ -76,11 +76,19 @@ namespace MediaBrowser.Providers.Savers builder.Append("" + SecurityElement.Escape(episode.IndexNumberEnd.Value.ToString(_usCulture)) + ""); } - if (episode.SpecialSeasonNumber.HasValue) + if (episode.AirsAfterSeasonNumber.HasValue) { - builder.Append("" + SecurityElement.Escape(episode.SpecialSeasonNumber.Value.ToString(_usCulture)) + ""); + builder.Append("" + SecurityElement.Escape(episode.AirsAfterSeasonNumber.Value.ToString(_usCulture)) + ""); } - + if (episode.AirsBeforeEpisodeNumber.HasValue) + { + builder.Append("" + SecurityElement.Escape(episode.AirsBeforeEpisodeNumber.Value.ToString(_usCulture)) + ""); + } + if (episode.AirsBeforeSeasonNumber.HasValue) + { + builder.Append("" + SecurityElement.Escape(episode.AirsBeforeSeasonNumber.Value.ToString(_usCulture)) + ""); + } + if (episode.ParentIndexNumber.HasValue) { builder.Append("" + SecurityElement.Escape(episode.ParentIndexNumber.Value.ToString(_usCulture)) + ""); @@ -105,7 +113,9 @@ namespace MediaBrowser.Providers.Savers "EpisodeNumber", "EpisodeName", "EpisodeNumberEnd", - "SpecialSeasonNumber" + "airsafter_season", + "airsbefore_episode", + "airsbefore_season" }); // Set last refreshed so that the provider doesn't trigger after the file save diff --git a/MediaBrowser.Providers/TV/EpisodeXmlParser.cs b/MediaBrowser.Providers/TV/EpisodeXmlParser.cs index 5188e0824b..5d970107e4 100644 --- a/MediaBrowser.Providers/TV/EpisodeXmlParser.cs +++ b/MediaBrowser.Providers/TV/EpisodeXmlParser.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; +using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -38,6 +39,8 @@ namespace MediaBrowser.Providers.TV } } + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + /// /// Fetches the data from XML node. /// @@ -139,19 +142,57 @@ namespace MediaBrowser.Providers.TV break; } - case "SpecialSeasonNumber": + case "airsbefore_episode": { - var number = reader.ReadElementContentAsString(); + var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(number)) + if (!string.IsNullOrWhiteSpace(val)) { - int num; + int rval; - if (int.TryParse(number, out num)) + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) { - item.SpecialSeasonNumber = num; + item.AirsBeforeEpisodeNumber = rval; } } + + break; + } + + case "airsafter_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) + { + item.AirsAfterSeasonNumber = rval; + } + } + + break; + } + + case "airsbefore_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) + { + item.AirsBeforeSeasonNumber = rval; + } + } + break; } diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs index ab824ac578..382c25863f 100644 --- a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs @@ -113,7 +113,7 @@ namespace MediaBrowser.Providers.TV { get { - return "3"; + return "5"; } } @@ -404,6 +404,24 @@ namespace MediaBrowser.Providers.TV break; } + case "airsbefore_episode": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.AirsBeforeEpisodeNumber = rval; + } + } + + break; + } + case "airsafter_season": { var val = reader.ReadElementContentAsString(); @@ -415,9 +433,10 @@ namespace MediaBrowser.Providers.TV // int.TryParse is local aware, so it can be probamatic, force us culture if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) { - item.SpecialSeasonNumber = rval; + item.AirsAfterSeasonNumber = rval; } } + break; } @@ -432,9 +451,10 @@ namespace MediaBrowser.Providers.TV // int.TryParse is local aware, so it can be probamatic, force us culture if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) { - item.SpecialSeasonNumber = rval; + item.AirsBeforeSeasonNumber = rval; } } + break; } diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 62c4c2e6fa..1f0e7d1e1e 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1029,7 +1029,7 @@ namespace MediaBrowser.Server.Implementations.Dto if (episode != null) { dto.IndexNumberEnd = episode.IndexNumberEnd; - dto.SpecialSeasonNumber = episode.SpecialSeasonNumber; + dto.SpecialSeasonNumber = episode.AirsAfterSeasonNumber ?? episode.AirsBeforeSeasonNumber; } // Add SeriesInfo diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs index a29a3d069f..b8874fb548 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs @@ -1,7 +1,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; -using MediaBrowser.Controller.IO; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; @@ -48,12 +47,12 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// /// The audio image resource pool /// - private readonly SemaphoreSlim _audioImageResourcePool = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim _audioImageResourcePool = new SemaphoreSlim(2, 2); /// /// The FF probe resource pool /// - private readonly SemaphoreSlim _ffProbeResourcePool = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim _ffProbeResourcePool = new SemaphoreSlim(2, 2); private readonly IFileSystem _fileSystem; public string FFMpegPath { get; private set; }