< Summary - Jellyfin

Information
Class: MediaBrowser.Controller.Entities.UserRootFolder
Assembly: MediaBrowser.Controller
File(s): /srv/git/jellyfin/MediaBrowser.Controller/Entities/UserRootFolder.cs
Line coverage
76%
Covered lines: 36
Uncovered lines: 11
Coverable lines: 47
Total lines: 147
Line coverage: 76.5%
Branch coverage
50%
Covered branches: 5
Total branches: 10
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 3/26/2026 - 12:14:14 AM Line coverage: 74.3% (29/39) Branch coverage: 66.6% (4/6) Total lines: 1414/19/2026 - 12:14:27 AM Line coverage: 78.2% (36/46) Branch coverage: 75% (6/8) Total lines: 1415/20/2026 - 12:15:44 AM Line coverage: 78.2% (36/46) Branch coverage: 62.5% (5/8) Total lines: 1416/18/2026 - 12:16:24 AM Line coverage: 76.5% (36/47) Branch coverage: 50% (5/10) Total lines: 147 3/26/2026 - 12:14:14 AM Line coverage: 74.3% (29/39) Branch coverage: 66.6% (4/6) Total lines: 1414/19/2026 - 12:14:27 AM Line coverage: 78.2% (36/46) Branch coverage: 75% (6/8) Total lines: 1415/20/2026 - 12:15:44 AM Line coverage: 78.2% (36/46) Branch coverage: 62.5% (5/8) Total lines: 1416/18/2026 - 12:16:24 AM Line coverage: 76.5% (36/47) Branch coverage: 50% (5/10) Total lines: 147

Coverage delta

Coverage delta 13 -13

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
get_SupportsInheritedParentImages()100%11100%
get_SupportsPlayedStatus()100%210%
get_SupportsShortcutChildren()100%11100%
get_IsPreSorted()100%210%
ClearCache()100%11100%
LoadChildren()100%22100%
GetItemsInternal(...)0%2040%
GetChildCount(...)100%11100%
GetEligibleChildrenForRecursiveChildren(...)100%11100%
BeforeMetadataRefresh(...)50%22100%
GetNonCachedChildren(...)100%11100%
ValidateChildrenInternal()100%22100%

File(s)

/srv/git/jellyfin/MediaBrowser.Controller/Entities/UserRootFolder.cs

#LineLine coverage
 1#nullable disable
 2
 3#pragma warning disable CS1591
 4
 5using System;
 6using System.Collections.Generic;
 7using System.Linq;
 8using System.Text.Json.Serialization;
 9using System.Threading;
 10using System.Threading.Tasks;
 11using Jellyfin.Database.Implementations.Entities;
 12using MediaBrowser.Controller.Providers;
 13using MediaBrowser.Model.Library;
 14using MediaBrowser.Model.Querying;
 15
 16namespace MediaBrowser.Controller.Entities
 17{
 18    /// <summary>
 19    /// Special class used for User Roots.  Children contain actual ones defined for this user
 20    /// PLUS the virtual folders from the physical root (added by plug-ins).
 21    /// </summary>
 22    public class UserRootFolder : Folder
 23    {
 4624        private readonly Lock _childIdsLock = new();
 25        private List<Guid> _childrenIds = null;
 26
 27        /// <summary>
 28        /// Initializes a new instance of the <see cref="UserRootFolder"/> class.
 29        /// </summary>
 4630        public UserRootFolder()
 31        {
 4632            IsRoot = true;
 4633        }
 34
 35        [JsonIgnore]
 636        public override bool SupportsInheritedParentImages => false;
 37
 38        [JsonIgnore]
 039        public override bool SupportsPlayedStatus => false;
 40
 41        [JsonIgnore]
 6642        protected override bool SupportsShortcutChildren => true;
 43
 44        [JsonIgnore]
 045        public override bool IsPreSorted => true;
 46
 47        private void ClearCache()
 8048        {
 49            lock (_childIdsLock)
 50            {
 8051                _childrenIds = null;
 8052            }
 8053        }
 54
 55        protected override IReadOnlyList<BaseItem> LoadChildren()
 9256        {
 57            lock (_childIdsLock)
 58            {
 9259                if (_childrenIds is null)
 60                {
 6561                    var list = base.LoadChildren();
 6562                    _childrenIds = list.Select(i => i.Id).ToList();
 6563                    return list;
 64                }
 65
 2766                return _childrenIds.Select(LibraryManager.GetItemById).Where(i => i is not null).ToList();
 67            }
 9268        }
 69
 70        protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
 71        {
 72            // The user root holds no items of its own - a plain listing returns the user's
 73            // views. But a request carrying any filter is a search across the libraries, so
 74            // resolve it through the recursive query path even when Recursive wasn't set;
 75            // otherwise the filters would be silently dropped. Recursive is set so the
 76            // downstream query (ancestor/top-parent scoping) treats it as a recursive search.
 077            if (query.Recursive || query.HasFilters)
 78            {
 079                query.Recursive = true;
 080                return QueryRecursive(query);
 81            }
 82
 083            var result = UserViewManager.GetUserViews(new UserViewQuery
 084            {
 085                User = query.User,
 086                PresetViews = query.PresetViews
 087            });
 88
 089            return UserViewBuilder.SortAndPage(result, null, query, LibraryManager);
 90        }
 91
 92        public override int GetChildCount(User user)
 93        {
 694            return GetChildren(user, true).Count;
 95        }
 96
 97        protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
 98        {
 1099            var list = base.GetEligibleChildrenForRecursiveChildren(user).ToList();
 10100            list.AddRange(LibraryManager.RootFolder.VirtualChildren);
 101
 10102            return list;
 103        }
 104
 105        public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
 106        {
 17107            ClearCache();
 17108            var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
 109
 17110            if (string.Equals("default", Name, StringComparison.OrdinalIgnoreCase))
 111            {
 17112                Name = "Media Folders";
 17113                hasChanges = true;
 114            }
 115
 17116            return hasChanges;
 117        }
 118
 119        protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
 120        {
 21121            ClearCache();
 122
 21123            return base.GetNonCachedChildren(directoryService);
 124        }
 125
 126        protected override async Task ValidateChildrenInternal(IProgress<double> progress, bool recursive, bool refreshC
 127        {
 21128            ClearCache();
 129
 21130            await base.ValidateChildrenInternal(progress, recursive, refreshChildMetadata, allowRemoveRoot, refreshOptio
 21131                .ConfigureAwait(false);
 132
 21133            ClearCache();
 134
 135            // Not the best way to handle this, but it solves an issue
 136            // CollectionFolders aren't always getting saved after changes
 137            // This means that grabbing the item by Id may end up returning the old one
 138            // Fix is in two places - make sure the folder gets saved
 139            // And here to remedy it for affected users.
 140            // In theory this can be removed eventually.
 58141            foreach (var item in Children)
 142            {
 8143                LibraryManager.RegisterItem(item);
 144            }
 21145        }
 146    }
 147}