using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.TV; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using System; using System.Collections.Generic; using System.Linq; using MediaBrowser.Controller.Configuration; namespace Emby.Server.Implementations.TV { public class TVSeriesManager : ITVSeriesManager { private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; private readonly ILibraryManager _libraryManager; private readonly IServerConfigurationManager _config; public TVSeriesManager(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager, IServerConfigurationManager config) { _userManager = userManager; _userDataManager = userDataManager; _libraryManager = libraryManager; _config = config; } public QueryResult GetNextUp(NextUpQuery request) { var user = _userManager.GetUserById(request.UserId); if (user == null) { throw new ArgumentException("User not found"); } var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId); string presentationUniqueKey = null; int? limit = null; if (!string.IsNullOrWhiteSpace(request.SeriesId)) { var series = _libraryManager.GetItemById(request.SeriesId); if (series != null) { presentationUniqueKey = GetUniqueSeriesKey(series); limit = 1; } } if (string.IsNullOrWhiteSpace(presentationUniqueKey) && limit.HasValue) { limit = limit.Value + 10; } var items = _libraryManager.GetItemList(new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(Series).Name }, SortBy = new[] { ItemSortBy.SeriesDatePlayed }, SortOrder = SortOrder.Descending, PresentationUniqueKey = presentationUniqueKey, Limit = limit, ParentId = parentIdGuid, Recursive = true, DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions { Fields = new List { } } }).Cast().Select(GetUniqueSeriesKey); // Avoid implicitly captured closure var episodes = GetNextUpEpisodes(request, user, items); return GetResult(episodes, null, request); } public QueryResult GetNextUp(NextUpQuery request, List parentsFolders) { var user = _userManager.GetUserById(request.UserId); if (user == null) { throw new ArgumentException("User not found"); } string presentationUniqueKey = null; int? limit = null; if (!string.IsNullOrWhiteSpace(request.SeriesId)) { var series = _libraryManager.GetItemById(request.SeriesId); if (series != null) { presentationUniqueKey = GetUniqueSeriesKey(series); limit = 1; } } if (string.IsNullOrWhiteSpace(presentationUniqueKey) && limit.HasValue) { limit = limit.Value + 10; } var items = _libraryManager.GetItemList(new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(Series).Name }, SortBy = new[] { ItemSortBy.SeriesDatePlayed }, SortOrder = SortOrder.Descending, PresentationUniqueKey = presentationUniqueKey, Limit = limit, DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions { Fields = new List { }, EnableImages = false } }, parentsFolders.Cast().ToList()).Cast().Select(GetUniqueSeriesKey); // Avoid implicitly captured closure var episodes = GetNextUpEpisodes(request, user, items); return GetResult(episodes, null, request); } public IEnumerable GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable seriesKeys) { // Avoid implicitly captured closure var currentUser = user; var allNextUp = seriesKeys .Select(i => GetNextUp(i, currentUser)); //allNextUp = allNextUp.OrderByDescending(i => i.Item1); // If viewing all next up for all series, remove first episodes // But if that returns empty, keep those first episodes (avoid completely empty view) var alwaysEnableFirstEpisode = !string.IsNullOrWhiteSpace(request.SeriesId); var isFirstItemAFirstEpisode = true; return allNextUp .Where(i => { if (alwaysEnableFirstEpisode || i.Item1 != DateTime.MinValue) { isFirstItemAFirstEpisode = false; return true; } if (isFirstItemAFirstEpisode) { return false; } return true; }) .Select(i => i.Item2()) .Where(i => i != null) .Take(request.Limit ?? int.MaxValue); } private string GetUniqueSeriesKey(BaseItem series) { return series.GetPresentationUniqueKey(); } /// /// Gets the next up. /// /// Task{Episode}. private Tuple> GetNextUp(string seriesKey, User user) { var enableSeriesPresentationKey = _config.Configuration.EnableSeriesPresentationUniqueKey; var lastWatchedEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey, SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null, IncludeItemTypes = new[] { typeof(Episode).Name }, SortBy = new[] { ItemSortBy.SortName }, SortOrder = SortOrder.Descending, IsPlayed = true, Limit = 1, ParentIndexNumberNotEquals = 0, DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions { Fields = new List { }, EnableImages = false } }).FirstOrDefault(); Func getEpisode = () => { return _libraryManager.GetItemList(new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey, SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null, IncludeItemTypes = new[] { typeof(Episode).Name }, SortBy = new[] { ItemSortBy.SortName }, SortOrder = SortOrder.Ascending, Limit = 1, IsPlayed = false, IsVirtualItem = false, ParentIndexNumberNotEquals = 0, MinSortName = lastWatchedEpisode == null ? null : lastWatchedEpisode.SortName }).Cast().FirstOrDefault(); }; if (lastWatchedEpisode != null) { var userData = _userDataManager.GetUserData(user, lastWatchedEpisode); var lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue.AddDays(1); return new Tuple>(lastWatchedDate, getEpisode); } // Return the first episode return new Tuple>(DateTime.MinValue, getEpisode); } private QueryResult GetResult(IEnumerable items, int? totalRecordLimit, NextUpQuery query) { var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray(); var totalCount = itemsArray.Length; if (query.Limit.HasValue) { itemsArray = itemsArray.Skip(query.StartIndex ?? 0).Take(query.Limit.Value).ToArray(); } else if (query.StartIndex.HasValue) { itemsArray = itemsArray.Skip(query.StartIndex.Value).ToArray(); } return new QueryResult { TotalRecordCount = totalCount, Items = itemsArray }; } } }