< Summary - Jellyfin

Information
Class: Jellyfin.Server.Implementations.Item.LinkedChildrenService
Assembly: Jellyfin.Server.Implementations
File(s): /srv/git/jellyfin/Jellyfin.Server.Implementations/Item/LinkedChildrenService.cs
Line coverage
24%
Covered lines: 21
Uncovered lines: 64
Coverable lines: 85
Total lines: 177
Line coverage: 24.7%
Branch coverage
16%
Covered branches: 2
Total branches: 12
Branch coverage: 16.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/4/2026 - 12:15:16 AM Line coverage: 24.6% (19/77) Branch coverage: 20% (2/10) Total lines: 1625/11/2026 - 12:15:59 AM Line coverage: 26.5% (21/79) Branch coverage: 20% (2/10) Total lines: 1665/30/2026 - 12:15:32 AM Line coverage: 24.7% (21/85) Branch coverage: 16.6% (2/12) Total lines: 177 5/4/2026 - 12:15:16 AM Line coverage: 24.6% (19/77) Branch coverage: 20% (2/10) Total lines: 1625/11/2026 - 12:15:59 AM Line coverage: 26.5% (21/79) Branch coverage: 20% (2/10) Total lines: 1665/30/2026 - 12:15:32 AM Line coverage: 24.7% (21/85) Branch coverage: 16.6% (2/12) Total lines: 177

Coverage delta

Coverage delta 4 -4

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetLinkedChildrenIds(...)0%620%
FindArtists(...)50%4489.47%
GetManualLinkedParentIds(...)0%620%
RerouteLinkedChildren(...)0%620%
UpsertLinkedChild(...)0%620%

File(s)

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

#LineLine coverage
 1#pragma warning disable RS0030 // Do not use banned APIs
 2#pragma warning disable CA1304 // Specify CultureInfo
 3#pragma warning disable CA1311 // Specify a culture or use an invariant version
 4
 5using System;
 6using System.Collections.Generic;
 7using System.Linq;
 8using Jellyfin.Data.Enums;
 9using Jellyfin.Database.Implementations;
 10using MediaBrowser.Controller.Entities.Audio;
 11using MediaBrowser.Controller.Persistence;
 12using Microsoft.EntityFrameworkCore;
 13using DbLinkedChildType = Jellyfin.Database.Implementations.Entities.LinkedChildType;
 14using LinkedChildType = MediaBrowser.Controller.Entities.LinkedChildType;
 15
 16namespace Jellyfin.Server.Implementations.Item;
 17
 18/// <summary>
 19/// Provides linked children query and manipulation operations.
 20/// </summary>
 21public class LinkedChildrenService : ILinkedChildrenService
 22{
 23    private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
 24    private readonly IItemTypeLookup _itemTypeLookup;
 25    private readonly IItemQueryHelpers _queryHelpers;
 26
 27    /// <summary>
 28    /// Initializes a new instance of the <see cref="LinkedChildrenService"/> class.
 29    /// </summary>
 30    /// <param name="dbProvider">The database context factory.</param>
 31    /// <param name="itemTypeLookup">The item type lookup.</param>
 32    /// <param name="queryHelpers">The shared query helpers.</param>
 33    public LinkedChildrenService(
 34        IDbContextFactory<JellyfinDbContext> dbProvider,
 35        IItemTypeLookup itemTypeLookup,
 36        IItemQueryHelpers queryHelpers)
 37    {
 2138        _dbProvider = dbProvider;
 2139        _itemTypeLookup = itemTypeLookup;
 2140        _queryHelpers = queryHelpers;
 2141    }
 42
 43    /// <inheritdoc/>
 44    public IReadOnlyList<Guid> GetLinkedChildrenIds(Guid parentId, int? childType = null)
 45    {
 046        using var dbContext = _dbProvider.CreateDbContext();
 47
 048        var query = dbContext.LinkedChildren
 049            .Where(lc => lc.ParentId.Equals(parentId));
 50
 051        if (childType.HasValue)
 52        {
 053            query = query.Where(lc => (int)lc.ChildType == childType.Value);
 54        }
 55
 056        return query
 057            .OrderBy(lc => lc.SortOrder)
 058            .Select(lc => lc.ChildId)
 059            .ToArray();
 060    }
 61
 62    /// <inheritdoc/>
 63    public IReadOnlyDictionary<string, MusicArtist[]> FindArtists(IReadOnlyList<string> artistNames)
 64    {
 1765        using var dbContext = _dbProvider.CreateDbContext();
 66
 1767        var lowerNames = artistNames.Select(n => n.ToLowerInvariant()).ToArray();
 1768        var artists = dbContext.BaseItems
 1769            .AsNoTracking()
 1770            .Where(e => e.Type == _itemTypeLookup.BaseItemKindNames[BaseItemKind.MusicArtist]!)
 1771            .Where(e => lowerNames.Contains(e.Name!.ToLower()))
 1772            .ToArray();
 73
 1774        var lookup = artists
 1775            .GroupBy(e => e.Name!, StringComparer.OrdinalIgnoreCase)
 1776            .ToDictionary(
 1777                g => g.Key,
 1778                g => g.Select(f => _queryHelpers.DeserializeBaseItem(f)).Where(dto => dto is not null).Cast<MusicArtist>
 1779                StringComparer.OrdinalIgnoreCase);
 80
 1781        var result = new Dictionary<string, MusicArtist[]>(artistNames.Count);
 3482        foreach (var name in artistNames)
 83        {
 084            if (lookup.TryGetValue(name, out var artistArray))
 85            {
 086                result[name] = artistArray;
 87            }
 88        }
 89
 1790        return result;
 1791    }
 92
 93    /// <inheritdoc/>
 94    public IReadOnlyList<Guid> GetManualLinkedParentIds(Guid childId, BaseItemKind? parentType = null)
 95    {
 096        using var context = _dbProvider.CreateDbContext();
 97
 098        var query = context.LinkedChildren
 099            .Where(lc => lc.ChildId == childId && lc.ChildType == DbLinkedChildType.Manual);
 100
 0101        if (parentType.HasValue)
 102        {
 0103            var parentTypeName = _itemTypeLookup.BaseItemKindNames[parentType.Value];
 0104            query = query.Join(
 0105                context.BaseItems
 0106                    .Where(item => item.Type == parentTypeName),
 0107                lc => lc.ParentId,
 0108                item => item.Id,
 0109                (lc, _) => lc);
 110        }
 111
 0112        return query.Select(lc => lc.ParentId).Distinct().ToList();
 0113    }
 114
 115    /// <inheritdoc/>
 116    public IReadOnlyList<Guid> RerouteLinkedChildren(Guid fromChildId, Guid toChildId)
 117    {
 0118        using var context = _dbProvider.CreateDbContext();
 119
 0120        var affectedParentIds = context.LinkedChildren
 0121            .Where(lc => lc.ChildId == fromChildId && lc.ChildType == DbLinkedChildType.Manual)
 0122            .Select(lc => lc.ParentId)
 0123            .Distinct()
 0124            .ToList();
 125
 0126        if (affectedParentIds.Count == 0)
 127        {
 0128            return affectedParentIds;
 129        }
 130
 0131        var parentsWithTarget = context.LinkedChildren
 0132            .Where(lc => lc.ChildId == toChildId && lc.ChildType == DbLinkedChildType.Manual)
 0133            .Select(lc => lc.ParentId)
 0134            .ToHashSet();
 135
 0136        context.LinkedChildren
 0137            .Where(lc => lc.ChildId == fromChildId
 0138                && lc.ChildType == DbLinkedChildType.Manual
 0139                && !parentsWithTarget.Contains(lc.ParentId))
 0140            .ExecuteUpdate(s => s.SetProperty(e => e.ChildId, toChildId));
 141
 0142        context.LinkedChildren
 0143            .Where(lc => lc.ChildId == fromChildId
 0144                && lc.ChildType == DbLinkedChildType.Manual
 0145                && parentsWithTarget.Contains(lc.ParentId))
 0146            .ExecuteDelete();
 147
 0148        return affectedParentIds;
 0149    }
 150
 151    /// <inheritdoc/>
 152    public void UpsertLinkedChild(Guid parentId, Guid childId, LinkedChildType childType)
 153    {
 0154        using var context = _dbProvider.CreateDbContext();
 155
 0156        var dbChildType = (DbLinkedChildType)childType;
 0157        var existingLink = context.LinkedChildren
 0158            .FirstOrDefault(lc => lc.ParentId == parentId && lc.ChildId == childId);
 159
 0160        if (existingLink is null)
 161        {
 0162            context.LinkedChildren.Add(new Jellyfin.Database.Implementations.Entities.LinkedChildEntity
 0163            {
 0164                ParentId = parentId,
 0165                ChildId = childId,
 0166                ChildType = dbChildType,
 0167                SortOrder = null
 0168            });
 169        }
 170        else
 171        {
 0172            existingLink.ChildType = dbChildType;
 173        }
 174
 0175        context.SaveChanges();
 0176    }
 177}