< Summary - Jellyfin

Information
Class: MediaBrowser.Controller.Entities.AggregateFolder
Assembly: MediaBrowser.Controller
File(s): /srv/git/jellyfin/MediaBrowser.Controller/Entities/AggregateFolder.cs
Line coverage
84%
Covered lines: 58
Uncovered lines: 11
Coverable lines: 69
Total lines: 222
Line coverage: 84%
Branch coverage
57%
Covered branches: 15
Total branches: 26
Branch coverage: 57.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/12/2026 - 12:15:57 AM Line coverage: 82.8% (53/64) Branch coverage: 58.3% (14/24) Total lines: 2045/20/2026 - 12:15:44 AM Line coverage: 82.8% (53/64) Branch coverage: 54.1% (13/24) Total lines: 2046/28/2026 - 12:15:35 AM Line coverage: 84.3% (54/64) Branch coverage: 62.5% (15/24) Total lines: 2046/29/2026 - 12:15:14 AM Line coverage: 82.8% (53/64) Branch coverage: 54.1% (13/24) Total lines: 2047/13/2026 - 12:13:16 AM Line coverage: 84.3% (54/64) Branch coverage: 62.5% (15/24) Total lines: 2047/14/2026 - 12:13:58 AM Line coverage: 82.8% (53/64) Branch coverage: 54.1% (13/24) Total lines: 2047/26/2026 - 12:17:51 AM Line coverage: 84.3% (54/64) Branch coverage: 62.5% (15/24) Total lines: 2047/29/2026 - 12:15:53 AM Line coverage: 82.8% (53/64) Branch coverage: 54.1% (13/24) Total lines: 2048/2/2026 - 12:17:28 AM Line coverage: 84.3% (54/64) Branch coverage: 62.5% (15/24) Total lines: 2048/4/2026 - 12:15:21 AM Line coverage: 82.8% (53/64) Branch coverage: 54.1% (13/24) Total lines: 2048/5/2026 - 12:17:09 AM Line coverage: 84.3% (54/64) Branch coverage: 62.5% (15/24) Total lines: 2048/6/2026 - 12:17:15 AM Line coverage: 82.8% (53/64) Branch coverage: 54.1% (13/24) Total lines: 20410/29/2026 - 12:17:11 AM Line coverage: 84% (58/69) Branch coverage: 57.6% (15/26) Total lines: 222 5/12/2026 - 12:15:57 AM Line coverage: 82.8% (53/64) Branch coverage: 58.3% (14/24) Total lines: 2045/20/2026 - 12:15:44 AM Line coverage: 82.8% (53/64) Branch coverage: 54.1% (13/24) Total lines: 2046/28/2026 - 12:15:35 AM Line coverage: 84.3% (54/64) Branch coverage: 62.5% (15/24) Total lines: 2046/29/2026 - 12:15:14 AM Line coverage: 82.8% (53/64) Branch coverage: 54.1% (13/24) Total lines: 2047/13/2026 - 12:13:16 AM Line coverage: 84.3% (54/64) Branch coverage: 62.5% (15/24) Total lines: 2047/14/2026 - 12:13:58 AM Line coverage: 82.8% (53/64) Branch coverage: 54.1% (13/24) Total lines: 2047/26/2026 - 12:17:51 AM Line coverage: 84.3% (54/64) Branch coverage: 62.5% (15/24) Total lines: 2047/29/2026 - 12:15:53 AM Line coverage: 82.8% (53/64) Branch coverage: 54.1% (13/24) Total lines: 2048/2/2026 - 12:17:28 AM Line coverage: 84.3% (54/64) Branch coverage: 62.5% (15/24) Total lines: 2048/4/2026 - 12:15:21 AM Line coverage: 82.8% (53/64) Branch coverage: 54.1% (13/24) Total lines: 2048/5/2026 - 12:17:09 AM Line coverage: 84.3% (54/64) Branch coverage: 62.5% (15/24) Total lines: 2048/6/2026 - 12:17:15 AM Line coverage: 82.8% (53/64) Branch coverage: 54.1% (13/24) Total lines: 20410/29/2026 - 12:17:11 AM Line coverage: 84% (58/69) Branch coverage: 57.6% (15/26) Total lines: 222

Coverage delta

Coverage delta 9 -9

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
get_VirtualChildren()100%11100%
get_IsPhysicalRoot()100%210%
get_SupportsPlayedStatus()100%210%
get_PhysicalLocations()100%11100%
get_Children()100%11100%
set_Children(...)100%22100%
CanDelete()100%210%
GetFileSystemChildren(...)100%11100%
LoadChildren()50%4485.71%
ClearCache()100%11100%
RequiresRefresh()83.33%66100%
BeforeMetadataRefresh(...)50%22100%
CreateResolveArgs(...)83.33%66100%
GetNonCachedChildren(...)100%11100%
ValidateChildrenInternal()100%11100%
AddVirtualChild(...)100%11100%
FindVirtualChild(...)0%4260%

File(s)

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

#LineLine coverage
 1#nullable disable
 2
 3#pragma warning disable CA1819, CS1591
 4
 5using System;
 6using System.Collections.Concurrent;
 7using System.Collections.Generic;
 8using System.Linq;
 9using System.Text.Json.Serialization;
 10using System.Threading;
 11using System.Threading.Tasks;
 12using Jellyfin.Extensions;
 13using MediaBrowser.Controller.IO;
 14using MediaBrowser.Controller.Library;
 15using MediaBrowser.Controller.Providers;
 16using MediaBrowser.Model.IO;
 17
 18namespace MediaBrowser.Controller.Entities
 19{
 20    /// <summary>
 21    /// Specialized folder that can have items added to it's children by external entities.
 22    /// Used for our RootFolder so plugins can add items.
 23    /// </summary>
 24    public class AggregateFolder : Folder
 25    {
 5926        private readonly Lock _childIdsLock = new();
 27
 28        /// <summary>
 29        /// The _virtual children.
 30        /// </summary>
 5931        private readonly ConcurrentBag<BaseItem> _virtualChildren = new ConcurrentBag<BaseItem>();
 32        private bool _requiresRefresh;
 33        private Guid[] _childrenIds = null;
 34
 5935        public AggregateFolder()
 36        {
 5937            PhysicalLocationsList = Array.Empty<string>();
 5938        }
 39
 40        /// <summary>
 41        /// Gets the virtual children.
 42        /// </summary>
 43        /// <value>The virtual children.</value>
 6644        public ConcurrentBag<BaseItem> VirtualChildren => _virtualChildren;
 45
 46        [JsonIgnore]
 047        public override bool IsPhysicalRoot => true;
 48
 49        [JsonIgnore]
 050        public override bool SupportsPlayedStatus => false;
 51
 52        [JsonIgnore]
 8953        public override string[] PhysicalLocations => PhysicalLocationsList;
 54
 55        public string[] PhysicalLocationsList { get; set; }
 56
 57        // Children caches the resolved items, _childrenIds the ids they were loaded from. Clearing
 58        // only the former sends the next read back through LoadChildren, which replays the stale
 59        // id list, so a caller invalidating this folder has to drop both.
 60        [JsonIgnore]
 61        public override IEnumerable<BaseItem> Children
 62        {
 18863            get => base.Children;
 64            set
 65            {
 18466                if (value is null)
 67                {
 15668                    ClearCache();
 69                }
 70
 18471                base.Children = value;
 18472            }
 73        }
 74
 75        public override bool CanDelete()
 76        {
 077            return false;
 78        }
 79
 80        protected override FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
 81        {
 4982            return CreateResolveArgs(directoryService, true).FileSystemChildren;
 83        }
 84
 85        protected override IReadOnlyList<BaseItem> LoadChildren()
 8686        {
 87            lock (_childIdsLock)
 88            {
 8689                if (_childrenIds is null || _childrenIds.Length == 0)
 90                {
 8691                    var list = base.LoadChildren();
 8692                    _childrenIds = list.Select(i => i.Id).ToArray();
 8693                    return list;
 94                }
 95
 096                return _childrenIds.Select(LibraryManager.GetItemById).Where(i => i is not null).ToList();
 97            }
 8698        }
 99
 100        private void ClearCache()
 350101        {
 102            lock (_childIdsLock)
 103            {
 350104                _childrenIds = null;
 350105            }
 350106        }
 107
 108        public override bool RequiresRefresh()
 109        {
 29110            var changed = base.RequiresRefresh() || _requiresRefresh;
 111
 29112            if (!changed)
 113            {
 28114                var locations = PhysicalLocations;
 115
 28116                var newLocations = CreateResolveArgs(new DirectoryService(FileSystem), false).PhysicalLocations;
 117
 28118                if (!locations.SequenceEqual(newLocations))
 119                {
 17120                    changed = true;
 121                }
 122            }
 123
 29124            return changed;
 125        }
 126
 127        public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
 128        {
 18129            ClearCache();
 130
 18131            var changed = base.BeforeMetadataRefresh(replaceAllMetadata) || _requiresRefresh;
 18132            _requiresRefresh = false;
 18133            return changed;
 134        }
 135
 136        private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService, bool setPhysicalLocations)
 137        {
 77138            ClearCache();
 139
 77140            var path = ContainingFolderPath;
 141
 77142            var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager)
 77143            {
 77144                FileInfo = FileSystem.GetDirectoryInfo(path)
 77145            };
 146
 147            // Gather child folder and files
 77148            if (args.IsDirectory)
 149            {
 150                // When resolving the root, we need it's grandchildren (children of user views)
 77151                var flattenFolderDepth = 2;
 152
 77153                var files = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, FileSystem, CollectionFol
 154
 155                // Need to remove subpaths that may have been resolved from shortcuts
 156                // Example: if \\server\movies exists, then strip out \\server\movies\action
 77157                files = LibraryManager.NormalizeRootPathList(files).ToArray();
 158
 77159                args.FileSystemChildren = files;
 160            }
 161
 77162            _requiresRefresh = _requiresRefresh || !args.PhysicalLocations.SequenceEqual(PhysicalLocations);
 77163            if (setPhysicalLocations)
 164            {
 49165                PhysicalLocationsList = args.PhysicalLocations;
 166            }
 167
 77168            return args;
 169        }
 170
 171        protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
 172        {
 49173            return base.GetNonCachedChildren(directoryService).Concat(_virtualChildren);
 174        }
 175
 176        protected override async Task ValidateChildrenInternal(IProgress<double> progress, bool recursive, bool refreshC
 177        {
 50178            ClearCache();
 179
 50180            await base.ValidateChildrenInternal(progress, recursive, refreshChildMetadata, allowRemoveRoot, refreshOptio
 50181                .ConfigureAwait(false);
 182
 49183            ClearCache();
 49184        }
 185
 186        /// <summary>
 187        /// Adds the virtual child.
 188        /// </summary>
 189        /// <param name="child">The child.</param>
 190        /// <exception cref="ArgumentNullException">Throws if child is null.</exception>
 191        public void AddVirtualChild(BaseItem child)
 192        {
 27193            ArgumentNullException.ThrowIfNull(child);
 194
 27195            _virtualChildren.Add(child);
 27196        }
 197
 198        /// <summary>
 199        /// Finds the virtual child.
 200        /// </summary>
 201        /// <param name="id">The id.</param>
 202        /// <returns>BaseItem.</returns>
 203        /// <exception cref="ArgumentNullException">The id is empty.</exception>
 204        public BaseItem FindVirtualChild(Guid id)
 205        {
 0206            if (id.IsEmpty())
 207            {
 0208                throw new ArgumentNullException(nameof(id));
 209            }
 210
 0211            foreach (var child in _virtualChildren)
 212            {
 0213                if (child.Id.Equals(id))
 214                {
 0215                    return child;
 216                }
 217            }
 218
 0219            return null;
 0220        }
 221    }
 222}