< Summary - Jellyfin

Information
Class: Jellyfin.Server.Implementations.Item.PeopleRepository
Assembly: Jellyfin.Server.Implementations
File(s): /srv/git/jellyfin/Jellyfin.Server.Implementations/Item/PeopleRepository.cs
Line coverage
1%
Covered lines: 1
Uncovered lines: 82
Coverable lines: 83
Total lines: 200
Line coverage: 1.2%
Branch coverage
0%
Covered branches: 0
Total branches: 44
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetPeople(...)0%2040%
GetPeopleNames(...)0%620%
UpdatePeople(...)0%2040%
Map(...)0%7280%
Map(...)100%210%
TranslateQuery(...)0%342180%
IsAlphaNumeric(...)0%7280%
IsValidPersonType(...)100%210%

File(s)

/srv/git/jellyfin/Jellyfin.Server.Implementations/Item/PeopleRepository.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Collections.Immutable;
 4using System.Linq;
 5using Jellyfin.Data.Enums;
 6using Jellyfin.Database.Implementations;
 7using Jellyfin.Database.Implementations.Entities;
 8using Jellyfin.Extensions;
 9using MediaBrowser.Controller.Entities;
 10using MediaBrowser.Controller.Persistence;
 11using Microsoft.EntityFrameworkCore;
 12
 13namespace Jellyfin.Server.Implementations.Item;
 14#pragma warning disable RS0030 // Do not use banned APIs
 15#pragma warning disable CA1304 // Specify CultureInfo
 16#pragma warning disable CA1311 // Specify a culture or use an invariant version
 17#pragma warning disable CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string compari
 18
 19/// <summary>
 20/// Manager for handling people.
 21/// </summary>
 22/// <param name="dbProvider">Efcore Factory.</param>
 23/// <param name="itemTypeLookup">Items lookup service.</param>
 24/// <remarks>
 25/// Initializes a new instance of the <see cref="PeopleRepository"/> class.
 26/// </remarks>
 27public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider, IItemTypeLookup itemTypeLookup) : IPeople
 28{
 2129    private readonly IDbContextFactory<JellyfinDbContext> _dbProvider = dbProvider;
 30
 31    /// <inheritdoc/>
 32    public IReadOnlyList<PersonInfo> GetPeople(InternalPeopleQuery filter)
 33    {
 034        using var context = _dbProvider.CreateDbContext();
 035        var dbQuery = TranslateQuery(context.Peoples.AsNoTracking(), context, filter);
 36
 37        // dbQuery = dbQuery.OrderBy(e => e.ListOrder);
 038        if (filter.Limit > 0)
 39        {
 040            dbQuery = dbQuery.Take(filter.Limit);
 41        }
 42
 43        // Include PeopleBaseItemMap
 044        if (!filter.ItemId.IsEmpty())
 45        {
 046            dbQuery = dbQuery.Include(p => p.BaseItems!.Where(m => m.ItemId == filter.ItemId));
 47        }
 48
 049        return dbQuery.AsEnumerable().Select(Map).ToArray();
 050    }
 51
 52    /// <inheritdoc/>
 53    public IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery filter)
 54    {
 055        using var context = _dbProvider.CreateDbContext();
 056        var dbQuery = TranslateQuery(context.Peoples.AsNoTracking(), context, filter);
 57
 58        // dbQuery = dbQuery.OrderBy(e => e.ListOrder);
 059        if (filter.Limit > 0)
 60        {
 061            dbQuery = dbQuery.Take(filter.Limit);
 62        }
 63
 064        return dbQuery.Select(e => e.Name).ToArray();
 065    }
 66
 67    /// <inheritdoc />
 68    public void UpdatePeople(Guid itemId, IReadOnlyList<PersonInfo> people)
 69    {
 070        using var context = _dbProvider.CreateDbContext();
 071        using var transaction = context.Database.BeginTransaction();
 72
 073        context.PeopleBaseItemMap.Where(e => e.ItemId == itemId).ExecuteDelete();
 74        // TODO: yes for __SOME__ reason there can be duplicates.
 075        foreach (var item in people.DistinctBy(e => e.Id))
 76        {
 077            var personEntity = Map(item);
 078            var existingEntity = context.Peoples.FirstOrDefault(e => e.Id == personEntity.Id);
 079            if (existingEntity is null)
 80            {
 081                context.Peoples.Add(personEntity);
 082                existingEntity = personEntity;
 83            }
 84
 085            context.PeopleBaseItemMap.Add(new PeopleBaseItemMap()
 086            {
 087                Item = null!,
 088                ItemId = itemId,
 089                People = existingEntity,
 090                PeopleId = existingEntity.Id,
 091                ListOrder = item.SortOrder,
 092                SortOrder = item.SortOrder,
 093                Role = item.Role
 094            });
 95        }
 96
 097        context.SaveChanges();
 098        transaction.Commit();
 099    }
 100
 101    private PersonInfo Map(People people)
 102    {
 0103        var mapping = people.BaseItems?.FirstOrDefault();
 0104        var personInfo = new PersonInfo()
 0105        {
 0106            Id = people.Id,
 0107            Name = people.Name,
 0108            Role = mapping?.Role,
 0109            SortOrder = mapping?.SortOrder
 0110        };
 0111        if (Enum.TryParse<PersonKind>(people.PersonType, out var kind))
 112        {
 0113            personInfo.Type = kind;
 114        }
 115
 0116        return personInfo;
 117    }
 118
 119    private People Map(PersonInfo people)
 120    {
 0121        var personInfo = new People()
 0122        {
 0123            Name = people.Name,
 0124            PersonType = people.Type.ToString(),
 0125            Id = people.Id,
 0126        };
 127
 0128        return personInfo;
 129    }
 130
 131    private IQueryable<People> TranslateQuery(IQueryable<People> query, JellyfinDbContext context, InternalPeopleQuery f
 132    {
 0133        if (filter.User is not null && filter.IsFavorite.HasValue)
 134        {
 0135            var personType = itemTypeLookup.BaseItemKindNames[BaseItemKind.Person];
 0136            query = query.Where(e => e.PersonType == personType)
 0137                .Where(e => context.BaseItems.Where(d => d.UserData!.Any(w => w.IsFavorite == filter.IsFavorite && w.Use
 0138                    .Select(f => f.Name).Contains(e.Name));
 139        }
 140
 0141        if (!filter.ItemId.IsEmpty())
 142        {
 0143            query = query.Where(e => e.BaseItems!.Any(w => w.ItemId.Equals(filter.ItemId)));
 144        }
 145
 0146        if (!filter.AppearsInItemId.IsEmpty())
 147        {
 0148            query = query.Where(e => e.BaseItems!.Any(w => w.ItemId.Equals(filter.AppearsInItemId)));
 149        }
 150
 0151        var queryPersonTypes = filter.PersonTypes.Where(IsValidPersonType).ToList();
 0152        if (queryPersonTypes.Count > 0)
 153        {
 0154            query = query.Where(e => queryPersonTypes.Contains(e.PersonType));
 155        }
 156
 0157        var queryExcludePersonTypes = filter.ExcludePersonTypes.Where(IsValidPersonType).ToList();
 158
 0159        if (queryExcludePersonTypes.Count > 0)
 160        {
 0161            query = query.Where(e => !queryPersonTypes.Contains(e.PersonType));
 162        }
 163
 0164        if (filter.MaxListOrder.HasValue && !filter.ItemId.IsEmpty())
 165        {
 0166            query = query.Where(e => e.BaseItems!.First(w => w.ItemId == filter.ItemId).ListOrder <= filter.MaxListOrder
 167        }
 168
 0169        if (!string.IsNullOrWhiteSpace(filter.NameContains))
 170        {
 0171            var nameContainsUpper = filter.NameContains.ToUpper();
 0172            query = query.Where(e => e.Name.ToUpper().Contains(nameContainsUpper));
 173        }
 174
 0175        return query;
 176    }
 177
 178    private bool IsAlphaNumeric(string str)
 179    {
 0180        if (string.IsNullOrWhiteSpace(str))
 181        {
 0182            return false;
 183        }
 184
 0185        for (int i = 0; i < str.Length; i++)
 186        {
 0187            if (!char.IsLetter(str[i]) && !char.IsNumber(str[i]))
 188            {
 0189                return false;
 190            }
 191        }
 192
 0193        return true;
 194    }
 195
 196    private bool IsValidPersonType(string value)
 197    {
 0198        return IsAlphaNumeric(value);
 199    }
 200}