using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; 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.IO; using System.Linq; using System.Threading.Tasks; namespace MediaBrowser.Api.UserLibrary { /// /// Class BaseItemsByNameService /// /// The type of the T item type. public abstract class BaseItemsByNameService : BaseApiService where TItemType : BaseItem { /// /// The _user manager /// protected readonly IUserManager UserManager; /// /// The library manager /// protected readonly ILibraryManager LibraryManager; protected readonly IUserDataRepository UserDataRepository; protected readonly IItemRepository ItemRepository; protected IDtoService DtoService { get; private set; } /// /// Initializes a new instance of the class. /// /// The user manager. /// The library manager. /// The user data repository. protected BaseItemsByNameService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepository, IDtoService dtoService) { UserManager = userManager; LibraryManager = libraryManager; UserDataRepository = userDataRepository; ItemRepository = itemRepository; DtoService = dtoService; } /// /// Gets the specified request. /// /// The request. /// Task{ItemsResult}. protected async Task GetResult(GetItemsByName request) { User user = null; BaseItem item; if (request.UserId.HasValue) { user = UserManager.GetUserById(request.UserId.Value); item = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : DtoService.GetItemByDtoId(request.ParentId, user.Id); } else { item = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : DtoService.GetItemByDtoId(request.ParentId); } IEnumerable items; if (item.IsFolder) { var folder = (Folder)item; if (request.UserId.HasValue) { items = request.Recursive ? folder.GetRecursiveChildren(user) : folder.GetChildren(user, true); } else { items = request.Recursive ? folder.RecursiveChildren: folder.Children; } } else { items = new[] { item }; } items = FilterItems(request, items); var extractedItems = GetAllItems(request, items); extractedItems = FilterItems(request, extractedItems, user); extractedItems = SortItems(request, extractedItems); var ibnItemsArray = extractedItems.ToArray(); IEnumerable> ibnItems = ibnItemsArray; var result = new ItemsResult { TotalRecordCount = ibnItemsArray.Length }; if (request.StartIndex.HasValue || request.Limit.HasValue) { if (request.StartIndex.HasValue) { ibnItems = ibnItems.Skip(request.StartIndex.Value); } if (request.Limit.HasValue) { ibnItems = ibnItems.Take(request.Limit.Value); } } var fields = request.GetItemFields().ToList(); var tasks = ibnItems.Select(i => GetDto(i, user, fields)); var resultItems = await Task.WhenAll(tasks).ConfigureAwait(false); result.Items = resultItems.Where(i => i != null).ToArray(); return result; } /// /// Filters the items. /// /// The request. /// The items. /// The user. /// IEnumerable{IbnStub}. private IEnumerable> FilterItems(GetItemsByName request, IEnumerable> items, User user) { if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater)) { items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.Name, StringComparison.CurrentCultureIgnoreCase) < 1); } var imageTypes = request.GetImageTypes().ToArray(); if (imageTypes.Length > 0) { items = items.Where(item => imageTypes.Any(imageType => ItemsService.HasImage(item.GetItem().Result, imageType))); } var filters = request.GetFilters().ToList(); if (filters.Count == 0) { return items; } items = items.AsParallel(); if (filters.Contains(ItemFilter.Dislikes)) { items = items.Where(i => { var userdata = i.GetUserItemData(UserDataRepository, user.Id).Result; return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; }); } if (filters.Contains(ItemFilter.Likes)) { items = items.Where(i => { var userdata = i.GetUserItemData(UserDataRepository, user.Id).Result; return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; }); } if (filters.Contains(ItemFilter.IsFavoriteOrLikes)) { items = items.Where(i => { var userdata = i.GetUserItemData(UserDataRepository, user.Id).Result; if (userdata == null) { return false; } var likes = userdata.Likes ?? false; var favorite = userdata.IsFavorite; return likes || favorite; }); } if (filters.Contains(ItemFilter.IsFavorite)) { items = items.Where(i => { var userdata = i.GetUserItemData(UserDataRepository, user.Id).Result; return userdata != null && userdata.Likes.HasValue && userdata.IsFavorite; }); } return items.AsEnumerable(); } /// /// Sorts the items. /// /// The request. /// The items. /// IEnumerable{BaseItem}. private IEnumerable> SortItems(GetItemsByName request, IEnumerable> items) { if (string.Equals(request.SortBy, "SortName", StringComparison.OrdinalIgnoreCase)) { if (request.SortOrder.HasValue && request.SortOrder.Value == Model.Entities.SortOrder.Descending) { items = items.OrderByDescending(i => i.Name); } else { items = items.OrderBy(i => i.Name); } } else if (string.Equals(request.SortBy, "Random", StringComparison.OrdinalIgnoreCase)) { if (request.SortOrder.HasValue && request.SortOrder.Value == Model.Entities.SortOrder.Descending) { items = items.OrderByDescending(i => Guid.NewGuid()); } else { items = items.OrderBy(i => Guid.NewGuid()); } } return items; } /// /// Filters the items. /// /// The request. /// The items. /// IEnumerable{BaseItem}. protected virtual IEnumerable FilterItems(GetItemsByName request, IEnumerable items) { // Exclude item types if (!string.IsNullOrEmpty(request.ExcludeItemTypes)) { var vals = request.ExcludeItemTypes.Split(','); items = items.Where(f => !vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase)); } // Include item types if (!string.IsNullOrEmpty(request.IncludeItemTypes)) { var vals = request.IncludeItemTypes.Split(','); items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase)); } // Include MediaTypes if (!string.IsNullOrEmpty(request.MediaTypes)) { var vals = request.MediaTypes.Split(','); items = items.Where(f => vals.Contains(f.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase)); } return items; } /// /// Gets all items. /// /// The request. /// The items. /// IEnumerable{Tuple{System.StringFunc{System.Int32}}}. protected abstract IEnumerable> GetAllItems(GetItemsByName request, IEnumerable items); /// /// Gets the dto. /// /// The stub. /// The user. /// The fields. /// Task{DtoBaseItem}. private async Task GetDto(IbnStub stub, User user, List fields) { BaseItem item; try { item = await stub.GetItem().ConfigureAwait(false); } catch (IOException ex) { Logger.ErrorException("Error getting IBN item {0}", ex, stub.Name); return null; } var dto = user == null ? await DtoService.GetBaseItemDto(item, fields).ConfigureAwait(false) : await DtoService.GetBaseItemDto(item, fields, user).ConfigureAwait(false); if (fields.Contains(ItemFields.ItemCounts)) { var items = stub.Items; dto.ChildCount = items.Count; dto.RecentlyAddedItemCount = items.Count(i => i.IsRecentlyAdded()); } return dto; } /// /// Gets the items. /// /// The user id. /// IEnumerable{BaseItem}. protected IEnumerable GetItems(Guid? userId) { if (userId.HasValue) { var user = UserManager.GetUserById(userId.Value); return UserManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user); } return LibraryManager.RootFolder.RecursiveChildren; } } /// /// Class GetItemsByName /// public class GetItemsByName : BaseItemsRequest, IReturn { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public Guid? UserId { get; set; } [ApiMember(Name = "NameStartsWithOrGreater", Description = "Optional filter by items whose name is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string NameStartsWithOrGreater { get; set; } /// /// What to sort the results by /// /// The sort by. [ApiMember(Name = "SortBy", Description = "Optional. Options: SortName", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string SortBy { get; set; } public GetItemsByName() { Recursive = true; } } public class IbnStub where T : BaseItem { private readonly Func> _childItemsFunction; private List _childItems; private readonly Func> _itemFunction; private Task _itemTask; public string Name; private UserItemData _userData; public List Items { get { return _childItems ?? (_childItems = _childItemsFunction().ToList()); } } public Task GetItem() { return _itemTask ?? (_itemTask = _itemFunction(Name)); } public async Task GetUserItemData(IUserDataRepository repo, Guid userId) { var item = await GetItem().ConfigureAwait(false); return _userData ?? (_userData = repo.GetUserData(userId, item.GetUserDataKey())); } public IbnStub(string name, Func> childItems, Func> item) { Name = name; _childItemsFunction = childItems; _itemFunction = item; } } }