< Summary - Jellyfin

Information
Class: MediaBrowser.Controller.Entities.Audio.MusicAlbum
Assembly: MediaBrowser.Controller
File(s): /srv/git/jellyfin/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
Line coverage
14%
Covered lines: 12
Uncovered lines: 71
Coverable lines: 83
Total lines: 239
Line coverage: 14.4%
Branch coverage
10%
Covered branches: 3
Total branches: 28
Branch coverage: 10.7%
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: 5% (4/79) Branch coverage: 0% (0/28) Total lines: 2277/27/2026 - 12:16:14 AM Line coverage: 6.3% (5/79) Branch coverage: 0% (0/28) Total lines: 2278/9/2026 - 12:16:58 AM Line coverage: 15.1% (12/79) Branch coverage: 10.7% (3/28) Total lines: 22710/29/2026 - 12:17:11 AM Line coverage: 14.4% (12/83) Branch coverage: 10.7% (3/28) Total lines: 239 5/12/2026 - 12:15:57 AM Line coverage: 5% (4/79) Branch coverage: 0% (0/28) Total lines: 2277/27/2026 - 12:16:14 AM Line coverage: 6.3% (5/79) Branch coverage: 0% (0/28) Total lines: 2278/9/2026 - 12:16:58 AM Line coverage: 15.1% (12/79) Branch coverage: 10.7% (3/28) Total lines: 22710/29/2026 - 12:17:11 AM Line coverage: 14.4% (12/83) Branch coverage: 10.7% (3/28) Total lines: 239

Coverage delta

Coverage delta 11 -11

Metrics

File(s)

/srv/git/jellyfin/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

#LineLine coverage
 1#nullable disable
 2
 3#pragma warning disable CA1721, CA1826, 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.Data;
 12using Jellyfin.Data.Enums;
 13using Jellyfin.Database.Implementations.Entities;
 14using Jellyfin.Database.Implementations.Enums;
 15using MediaBrowser.Controller.Dto;
 16using MediaBrowser.Controller.Library;
 17using MediaBrowser.Controller.Providers;
 18using MediaBrowser.Model.Entities;
 19using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
 20
 21namespace MediaBrowser.Controller.Entities.Audio
 22{
 23    /// <summary>
 24    /// Class MusicAlbum.
 25    /// </summary>
 26    [Common.RequiresSourceSerialisation]
 27    public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>, IMetadata
 28    {
 1829        public MusicAlbum()
 30        {
 1831            Artists = Array.Empty<string>();
 1832            AlbumArtists = Array.Empty<string>();
 1833        }
 34
 35        /// <inheritdoc />
 36        public IReadOnlyList<string> AlbumArtists { get; set; }
 37
 38        /// <inheritdoc />
 39        public IReadOnlyList<string> Artists { get; set; }
 40
 41        [JsonIgnore]
 042        public override bool SupportsAddingToPlaylist => true;
 43
 44        [JsonIgnore]
 445        public override bool SupportsInheritedParentImages => true;
 46
 47        [JsonIgnore]
 048        public MusicArtist MusicArtist => GetMusicArtist(new DtoOptions(true));
 49
 50        [JsonIgnore]
 051        public override bool SupportsPlayedStatus => false;
 52
 53        [JsonIgnore]
 054        public override bool SupportsCumulativeRunTimeTicks => true;
 55
 56        [JsonIgnore]
 257        public string AlbumArtist => AlbumArtists.FirstOrDefault();
 58
 59        [JsonIgnore]
 260        public override bool SupportsPeople => true;
 61
 62        /// <summary>
 63        /// Gets the tracks.
 64        /// </summary>
 65        /// <value>The tracks.</value>
 66        [JsonIgnore]
 067        public IEnumerable<Audio> Tracks => GetRecursiveChildren(i => i is Audio).Cast<Audio>();
 68
 69        public MusicArtist GetMusicArtist(DtoOptions options)
 70        {
 271            var parents = GetParents();
 472            foreach (var parent in parents)
 73            {
 074                if (parent is MusicArtist artist)
 75                {
 076                    return artist;
 77                }
 78            }
 79
 280            var name = AlbumArtist;
 281            if (!string.IsNullOrEmpty(name))
 82            {
 083                return LibraryManager.GetArtist(name, options);
 84            }
 85
 286            return null;
 087        }
 88
 89        protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
 90        {
 091            return Tracks;
 92        }
 93
 94        public override double GetDefaultPrimaryImageAspectRatio()
 95        {
 096            return 1;
 97        }
 98
 99        public override List<string> GetUserDataKeys()
 100        {
 0101            var list = base.GetUserDataKeys();
 102
 0103            var albumArtist = AlbumArtist;
 0104            if (!string.IsNullOrEmpty(albumArtist))
 105            {
 0106                list.Insert(0, albumArtist + "-" + Name);
 107            }
 108
 0109            var id = this.GetProviderId(MetadataProvider.MusicBrainzAlbum);
 110
 0111            if (!string.IsNullOrEmpty(id))
 112            {
 0113                list.Insert(0, "MusicAlbum-Musicbrainz-" + id);
 114            }
 115
 0116            id = this.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup);
 117
 0118            if (!string.IsNullOrEmpty(id))
 119            {
 0120                list.Insert(0, "MusicAlbum-MusicBrainzReleaseGroup-" + id);
 121            }
 122
 0123            return list;
 124        }
 125
 126        protected override bool GetBlockUnratedValue(User user)
 127        {
 0128            return user.GetPreferenceValues<UnratedItem>(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music);
 129        }
 130
 131        public override UnratedItem GetBlockUnratedType()
 132        {
 0133            return UnratedItem.Music;
 134        }
 135
 136        public AlbumInfo GetLookupInfo()
 137        {
 0138            var id = GetItemLookupInfo<AlbumInfo>();
 139
 0140            id.AlbumArtists = AlbumArtists;
 141
 0142            var artist = GetMusicArtist(new DtoOptions(false));
 143
 0144            if (artist is not null)
 145            {
 0146                id.ArtistProviderIds = artist.ProviderIds;
 147            }
 148
 0149            id.SongInfos = GetRecursiveChildren(i => i is Audio)
 0150                .Cast<Audio>()
 0151                .Select(i => i.GetLookupInfo())
 0152                .ToList();
 153
 0154            var album = id.SongInfos
 0155                .Select(i => i.Album)
 0156                .FirstOrDefault(i => !string.IsNullOrEmpty(i));
 157
 0158            if (!string.IsNullOrEmpty(album))
 159            {
 0160                id.Name = album;
 161            }
 162
 0163            return id;
 164        }
 165
 166        public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, Cancella
 167        {
 168            try
 169            {
 0170                await RefreshAllMetadataInternal(refreshOptions, progress, cancellationToken).ConfigureAwait(false);
 0171            }
 172            finally
 173            {
 0174                ReleaseCachedChildren();
 175            }
 0176        }
 177
 178        private async Task RefreshAllMetadataInternal(MetadataRefreshOptions refreshOptions, IProgress<double> progress,
 179        {
 0180            var items = GetRecursiveChildren();
 181
 0182            var totalItems = items.Count;
 0183            var numComplete = 0;
 184
 0185            var childUpdateType = ItemUpdateType.None;
 186
 0187            foreach (var item in items)
 188            {
 0189                cancellationToken.ThrowIfCancellationRequested();
 190
 0191                var updateType = await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
 0192                childUpdateType = childUpdateType | updateType;
 193
 0194                numComplete++;
 0195                double percent = numComplete;
 0196                percent /= totalItems;
 0197                progress.Report(percent * 95);
 198            }
 199
 0200            var parentRefreshOptions = refreshOptions;
 0201            if (childUpdateType > ItemUpdateType.None)
 202            {
 0203                parentRefreshOptions = new MetadataRefreshOptions(refreshOptions)
 0204                {
 0205                    MetadataRefreshMode = MetadataRefreshMode.FullRefresh
 0206                };
 207            }
 208
 209            // Refresh current item
 0210            await RefreshMetadata(parentRefreshOptions, cancellationToken).ConfigureAwait(false);
 211
 0212            if (!refreshOptions.IsAutomated)
 213            {
 0214                await RefreshArtists(refreshOptions, cancellationToken).ConfigureAwait(false);
 215            }
 0216        }
 217
 218        private async Task RefreshArtists(MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
 219        {
 0220            foreach (var i in this.GetAllArtists())
 221            {
 222                // This should not be necessary but we're seeing some cases of it
 0223                if (string.IsNullOrEmpty(i))
 224                {
 225                    continue;
 226                }
 227
 0228                var artist = LibraryManager.GetArtist(i);
 229
 0230                if (!artist.IsAccessedByName)
 231                {
 232                    continue;
 233                }
 234
 0235                await artist.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
 236            }
 0237        }
 238    }
 239}