using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using ServiceStack.ServiceHost;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
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: AudioInfo, Budget, Chapters, DateCreated, DisplayMediaType, DisplayPreferences, EndDate, Genres, HomePageUrl, ItemCounts, IndexOptions, Locations, MediaStreams, Overview, OverviewHtml, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SeriesInfo, SortName, Studios, Taglines, TrailerUrls, UserData", 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)Enum.Parse(typeof(ItemFields), v, true));
}
}
///
/// 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;
///
/// 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)
{
_userManager = userManager;
_userDataRepository = userDataRepository;
_libraryManager = libraryManager;
}
///
/// Gets the specified request.
///
/// The request.
/// System.Object.
public object Get(GetNextUpEpisodes request)
{
var result = GetNextUpEpisodes(request).Result;
return ToOptimizedResult(result);
}
///
/// Gets the next up episodes.
///
/// The request.
/// Task{ItemsResult}.
private async Task GetNextUpEpisodes(GetNextUpEpisodes request)
{
var user = _userManager.GetUserById(request.UserId);
var tasks = user.RootFolder
.GetRecursiveChildren(user)
.OfType()
.AsParallel()
.Select(i => GetNextUp(i, user));
var itemsArray = await Task.WhenAll(tasks).ConfigureAwait(false);
itemsArray = itemsArray
.Where(i => i.Item1 != null)
.OrderByDescending(i =>
{
var seriesUserData =
_userDataRepository.GetUserData(user.Id, i.Item1.Series.GetUserDataKey()).Result;
if (seriesUserData.IsFavorite)
{
return 2;
}
if (seriesUserData.Likes.HasValue)
{
return seriesUserData.Likes.Value ? 1 : -1;
}
return 0;
})
.ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue)
.ToArray();
var pagedItems = ApplyPaging(request, itemsArray.Select(i => i.Item1));
var fields = request.GetItemFields().ToList();
var returnItems = await GetItemDtos(pagedItems, user, fields).ConfigureAwait(false);
return new ItemsResult
{
TotalRecordCount = itemsArray.Length,
Items = returnItems
};
}
///
/// Gets the next up.
///
/// The series.
/// The user.
/// Task{Episode}.
private async Task> 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 = await _userDataRepository.GetUserData(user.Id, episode.GetUserDataKey()).ConfigureAwait(false);
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);
}
///
/// Gets the item dtos.
///
/// The paged items.
/// The user.
/// The fields.
/// Task.
private Task GetItemDtos(IEnumerable pagedItems, User user, List fields)
{
var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
return Task.WhenAll(pagedItems.Select(i => dtoBuilder.GetBaseItemDto(i, fields, user)));
}
///
/// 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;
}
}
}