< 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: 19
Uncovered lines: 58
Coverable lines: 77
Total lines: 162
Line coverage: 24.6%
Branch coverage
20%
Covered branches: 2
Total branches: 10
Branch coverage: 20%
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: 162 5/4/2026 - 12:15:16 AM Line coverage: 24.6% (19/77) Branch coverage: 20% (2/10) Total lines: 162

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetLinkedChildrenIds(...)0%620%
FindArtists(...)50%4488.23%
GetManualLinkedParentIds(...)100%210%
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
 3using System;
 4using System.Collections.Generic;
 5using System.Linq;
 6using Jellyfin.Data.Enums;
 7using Jellyfin.Database.Implementations;
 8using MediaBrowser.Controller.Entities.Audio;
 9using MediaBrowser.Controller.Persistence;
 10using Microsoft.EntityFrameworkCore;
 11using DbLinkedChildType = Jellyfin.Database.Implementations.Entities.LinkedChildType;
 12using LinkedChildType = MediaBrowser.Controller.Entities.LinkedChildType;
 13
 14namespace Jellyfin.Server.Implementations.Item;
 15
 16/// <summary>
 17/// Provides linked children query and manipulation operations.
 18/// </summary>
 19public class LinkedChildrenService : ILinkedChildrenService
 20{
 21    private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
 22    private readonly IItemTypeLookup _itemTypeLookup;
 23    private readonly IItemQueryHelpers _queryHelpers;
 24
 25    /// <summary>
 26    /// Initializes a new instance of the <see cref="LinkedChildrenService"/> class.
 27    /// </summary>
 28    /// <param name="dbProvider">The database context factory.</param>
 29    /// <param name="itemTypeLookup">The item type lookup.</param>
 30    /// <param name="queryHelpers">The shared query helpers.</param>
 31    public LinkedChildrenService(
 32        IDbContextFactory<JellyfinDbContext> dbProvider,
 33        IItemTypeLookup itemTypeLookup,
 34        IItemQueryHelpers queryHelpers)
 35    {
 2136        _dbProvider = dbProvider;
 2137        _itemTypeLookup = itemTypeLookup;
 2138        _queryHelpers = queryHelpers;
 2139    }
 40
 41    /// <inheritdoc/>
 42    public IReadOnlyList<Guid> GetLinkedChildrenIds(Guid parentId, int? childType = null)
 43    {
 044        using var dbContext = _dbProvider.CreateDbContext();
 45
 046        var query = dbContext.LinkedChildren
 047            .Where(lc => lc.ParentId.Equals(parentId));
 48
 049        if (childType.HasValue)
 50        {
 051            query = query.Where(lc => (int)lc.ChildType == childType.Value);
 52        }
 53
 054        return query
 055            .OrderBy(lc => lc.SortOrder)
 056            .Select(lc => lc.ChildId)
 057            .ToArray();
 058    }
 59
 60    /// <inheritdoc/>
 61    public IReadOnlyDictionary<string, MusicArtist[]> FindArtists(IReadOnlyList<string> artistNames)
 62    {
 1763        using var dbContext = _dbProvider.CreateDbContext();
 64
 1765        var artists = dbContext.BaseItems
 1766            .AsNoTracking()
 1767            .Where(e => e.Type == _itemTypeLookup.BaseItemKindNames[BaseItemKind.MusicArtist]!)
 1768            .Where(e => artistNames.Contains(e.Name))
 1769            .ToArray();
 70
 1771        var lookup = artists
 1772            .GroupBy(e => e.Name!)
 1773            .ToDictionary(
 1774                g => g.Key,
 1775                g => g.Select(f => _queryHelpers.DeserializeBaseItem(f)).Where(dto => dto is not null).Cast<MusicArtist>
 76
 1777        var result = new Dictionary<string, MusicArtist[]>(artistNames.Count);
 3478        foreach (var name in artistNames)
 79        {
 080            if (lookup.TryGetValue(name, out var artistArray))
 81            {
 082                result[name] = artistArray;
 83            }
 84        }
 85
 1786        return result;
 1787    }
 88
 89    /// <inheritdoc/>
 90    public IReadOnlyList<Guid> GetManualLinkedParentIds(Guid childId)
 91    {
 092        using var context = _dbProvider.CreateDbContext();
 093        return context.LinkedChildren
 094            .Where(lc => lc.ChildId == childId && lc.ChildType == DbLinkedChildType.Manual)
 095            .Select(lc => lc.ParentId)
 096            .Distinct()
 097            .ToList();
 098    }
 99
 100    /// <inheritdoc/>
 101    public IReadOnlyList<Guid> RerouteLinkedChildren(Guid fromChildId, Guid toChildId)
 102    {
 0103        using var context = _dbProvider.CreateDbContext();
 104
 0105        var affectedParentIds = context.LinkedChildren
 0106            .Where(lc => lc.ChildId == fromChildId && lc.ChildType == DbLinkedChildType.Manual)
 0107            .Select(lc => lc.ParentId)
 0108            .Distinct()
 0109            .ToList();
 110
 0111        if (affectedParentIds.Count == 0)
 112        {
 0113            return affectedParentIds;
 114        }
 115
 0116        var parentsWithTarget = context.LinkedChildren
 0117            .Where(lc => lc.ChildId == toChildId && lc.ChildType == DbLinkedChildType.Manual)
 0118            .Select(lc => lc.ParentId)
 0119            .ToHashSet();
 120
 0121        context.LinkedChildren
 0122            .Where(lc => lc.ChildId == fromChildId
 0123                && lc.ChildType == DbLinkedChildType.Manual
 0124                && !parentsWithTarget.Contains(lc.ParentId))
 0125            .ExecuteUpdate(s => s.SetProperty(e => e.ChildId, toChildId));
 126
 0127        context.LinkedChildren
 0128            .Where(lc => lc.ChildId == fromChildId
 0129                && lc.ChildType == DbLinkedChildType.Manual
 0130                && parentsWithTarget.Contains(lc.ParentId))
 0131            .ExecuteDelete();
 132
 0133        return affectedParentIds;
 0134    }
 135
 136    /// <inheritdoc/>
 137    public void UpsertLinkedChild(Guid parentId, Guid childId, LinkedChildType childType)
 138    {
 0139        using var context = _dbProvider.CreateDbContext();
 140
 0141        var dbChildType = (DbLinkedChildType)childType;
 0142        var existingLink = context.LinkedChildren
 0143            .FirstOrDefault(lc => lc.ParentId == parentId && lc.ChildId == childId);
 144
 0145        if (existingLink is null)
 146        {
 0147            context.LinkedChildren.Add(new Jellyfin.Database.Implementations.Entities.LinkedChildEntity
 0148            {
 0149                ParentId = parentId,
 0150                ChildId = childId,
 0151                ChildType = dbChildType,
 0152                SortOrder = null
 0153            });
 154        }
 155        else
 156        {
 0157            existingLink.ChildType = dbChildType;
 158        }
 159
 0160        context.SaveChanges();
 0161    }
 162}