using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Querying; using ServiceStack.ServiceHost; using System; using System.Collections.Generic; using System.Linq; namespace MediaBrowser.Api { /// /// Class GetNextUpEpisodes /// [Route("/Shows/NextUp", "GET")] [Api(("Gets a list of currently installed plugins"))] public class GetNextUpEpisodes : IReturn { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] public Guid UserId { get; set; } /// /// Skips over a given number of items within the results. Use for paging. /// /// The start index. [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? StartIndex { get; set; } /// /// The maximum number of items to return /// /// The limit. [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Limit { get; set; } /// /// Fields to return within the items, in addition to basic information /// /// The fields. [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, ItemCounts, IndexOptions, MediaStreams, Overview, OverviewHtml, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } /// /// Gets the item fields. /// /// IEnumerable{ItemFields}. public IEnumerable GetItemFields() { var val = Fields; if (string.IsNullOrEmpty(val)) { return new ItemFields[] { }; } return val.Split(',').Select(v => { ItemFields value; if (Enum.TryParse(v, true, out value)) { return (ItemFields?)value; } return null; }).Where(i => i.HasValue).Select(i => i.Value); } } [Route("/Shows/{Id}/Similar", "GET")] [Api(Description = "Finds tv shows similar to a given one.")] public class GetSimilarShows : BaseGetSimilarItemsFromItem { } /// /// Class TvShowsService /// public class TvShowsService : BaseApiService { /// /// The _user manager /// private readonly IUserManager _userManager; /// /// The _user data repository /// private readonly IUserDataRepository _userDataRepository; /// /// The _library manager /// private readonly ILibraryManager _libraryManager; private readonly IItemRepository _itemRepo; private readonly IDtoService _dtoService; /// /// Initializes a new instance of the class. /// /// The user manager. /// The user data repository. /// The library manager. public TvShowsService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; _itemRepo = itemRepo; _dtoService = dtoService; } /// /// Gets the specified request. /// /// The request. /// System.Object. public object Get(GetSimilarShows request) { var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager, _itemRepo, _libraryManager, _userDataRepository, _dtoService, Logger, request, item => item is Series, SimilarItemsHelper.GetSimiliarityScore); return ToOptimizedResult(result); } /// /// Gets the specified request. /// /// The request. /// System.Object. public object Get(GetNextUpEpisodes request) { var result = GetNextUpEpisodes(request); return ToOptimizedResult(result); } /// /// Gets the next up episodes. /// /// The request. /// Task{ItemsResult}. private ItemsResult GetNextUpEpisodes(GetNextUpEpisodes request) { var user = _userManager.GetUserById(request.UserId); var itemsList = user.RootFolder .GetRecursiveChildren(user) .OfType() .AsParallel() .Select(i => GetNextUp(i, user)) .ToList(); itemsList = itemsList .Where(i => i.Item1 != null) .OrderByDescending(i => { var seriesUserData = _userDataRepository.GetUserData(user.Id, i.Item1.Series.GetUserDataKey()); if (seriesUserData.IsFavorite) { return 2; } if (seriesUserData.Likes.HasValue) { return seriesUserData.Likes.Value ? 1 : -1; } 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 }; } /// /// Gets the next up. /// /// The series. /// The user. /// Task{Episode}. private Tuple GetNextUp(Series series, User user) { var allEpisodes = series.GetRecursiveChildren(user) .OfType() .OrderByDescending(i => i.PremiereDate ?? DateTime.MinValue) .ThenByDescending(i => i.IndexNumber ?? 0) .ToList(); Episode lastWatched = null; var lastWatchedDate = DateTime.MinValue; Episode nextUp = null; // Go back starting with the most recent episodes foreach (var episode in allEpisodes) { var userData = _userDataRepository.GetUserData(user.Id, episode.GetUserDataKey()); if (userData.Played) { if (lastWatched != null || nextUp == null) { break; } lastWatched = episode; lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue; } else { nextUp = episode; } } if (lastWatched != null) { return new Tuple(nextUp, lastWatchedDate); } return new Tuple(null, lastWatchedDate); } /// /// Applies the paging. /// /// The request. /// The items. /// IEnumerable{BaseItem}. private IEnumerable ApplyPaging(GetNextUpEpisodes request, IEnumerable items) { // Start at if (request.StartIndex.HasValue) { items = items.Skip(request.StartIndex.Value); } // Return limit if (request.Limit.HasValue) { items = items.Take(request.Limit.Value); } return items; } } }