using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Persistence; using Microsoft.EntityFrameworkCore; namespace Jellyfin.Server.Implementations.Item; #pragma warning disable RS0030 // Do not use banned APIs /// /// Manager for handling people. /// /// Efcore Factory. /// /// Initializes a new instance of the class. /// public class PeopleRepository(IDbContextFactory dbProvider) : IPeopleRepository { private readonly IDbContextFactory _dbProvider = dbProvider; /// public IReadOnlyList GetPeople(InternalPeopleQuery filter) { using var context = _dbProvider.CreateDbContext(); var dbQuery = TranslateQuery(context.Peoples.AsNoTracking(), context, filter); // dbQuery = dbQuery.OrderBy(e => e.ListOrder); if (filter.Limit > 0) { dbQuery = dbQuery.Take(filter.Limit); } return dbQuery.AsEnumerable().Select(Map).ToImmutableArray(); } /// public IReadOnlyList GetPeopleNames(InternalPeopleQuery filter) { using var context = _dbProvider.CreateDbContext(); var dbQuery = TranslateQuery(context.Peoples.AsNoTracking(), context, filter); // dbQuery = dbQuery.OrderBy(e => e.ListOrder); if (filter.Limit > 0) { dbQuery = dbQuery.Take(filter.Limit); } return dbQuery.Select(e => e.Name).ToImmutableArray(); } /// public void UpdatePeople(Guid itemId, IReadOnlyList people) { using var context = _dbProvider.CreateDbContext(); using var transaction = context.Database.BeginTransaction(); context.PeopleBaseItemMap.Where(e => e.ItemId == itemId).ExecuteDelete(); foreach (var item in people) { var personEntity = Map(item); var existingEntity = context.Peoples.FirstOrDefault(e => e.Id == personEntity.Id); if (existingEntity is null) { context.Peoples.Add(personEntity); existingEntity = personEntity; } context.PeopleBaseItemMap.Add(new PeopleBaseItemMap() { Item = null!, ItemId = itemId, People = existingEntity, PeopleId = existingEntity.Id, ListOrder = item.SortOrder, SortOrder = item.SortOrder, Role = item.Role }); } context.Peoples.AddRange(people.Select(Map)); context.SaveChanges(); transaction.Commit(); } private PersonInfo Map(People people) { var personInfo = new PersonInfo() { Id = people.Id, Name = people.Name, }; if (Enum.TryParse(people.PersonType, out var kind)) { personInfo.Type = kind; } return personInfo; } private People Map(PersonInfo people) { var personInfo = new People() { Name = people.Name, PersonType = people.Type.ToString(), Id = people.Id, }; return personInfo; } private IQueryable TranslateQuery(IQueryable query, JellyfinDbContext context, InternalPeopleQuery filter) { if (filter.User is not null && filter.IsFavorite.HasValue) { query = query.Where(e => e.PersonType == typeof(Person).FullName) .Where(e => context.BaseItems.Where(d => context.UserData.Where(e => e.IsFavorite == filter.IsFavorite && e.UserId.Equals(filter.User.Id)).Any(f => f.Key == d.UserDataKey)) .Select(f => f.Name).Contains(e.Name)); } if (!filter.ItemId.IsEmpty()) { query = query.Where(e => e.BaseItems!.Any(w => w.ItemId.Equals(filter.ItemId))); } if (!filter.AppearsInItemId.IsEmpty()) { query = query.Where(e => e.BaseItems!.Any(w => w.ItemId.Equals(filter.AppearsInItemId))); } var queryPersonTypes = filter.PersonTypes.Where(IsValidPersonType).ToList(); if (queryPersonTypes.Count > 0) { query = query.Where(e => queryPersonTypes.Contains(e.PersonType)); } var queryExcludePersonTypes = filter.ExcludePersonTypes.Where(IsValidPersonType).ToList(); if (queryExcludePersonTypes.Count > 0) { query = query.Where(e => !queryPersonTypes.Contains(e.PersonType)); } if (filter.MaxListOrder.HasValue && !filter.ItemId.IsEmpty()) { query = query.Where(e => e.BaseItems!.First(w => w.ItemId == filter.ItemId).ListOrder <= filter.MaxListOrder.Value); } if (!string.IsNullOrWhiteSpace(filter.NameContains)) { query = query.Where(e => e.Name.Contains(filter.NameContains)); } return query; } private bool IsAlphaNumeric(string str) { if (string.IsNullOrWhiteSpace(str)) { return false; } for (int i = 0; i < str.Length; i++) { if (!char.IsLetter(str[i]) && !char.IsNumber(str[i])) { return false; } } return true; } private bool IsValidPersonType(string value) { return IsAlphaNumeric(value); } }