< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.Library.Resolvers.Audio.MusicAlbumResolver
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
Line coverage
17%
Covered lines: 10
Uncovered lines: 46
Coverable lines: 56
Total lines: 181
Line coverage: 17.8%
Branch coverage
4%
Covered branches: 1
Total branches: 24
Branch coverage: 4.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Priority()100%11100%
Resolve(...)10%35.771036.36%
IsMusicAlbum(...)100%210%
IsMusicAlbum(...)0%7280%
ContainsMusic(...)0%4260%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs

#LineLine coverage
 1#nullable disable
 2
 3using System;
 4using System.Collections.Generic;
 5using System.IO;
 6using System.Linq;
 7using System.Threading;
 8using System.Threading.Tasks;
 9using Emby.Naming.Audio;
 10using Emby.Naming.Common;
 11using Jellyfin.Data.Enums;
 12using MediaBrowser.Controller.Entities.Audio;
 13using MediaBrowser.Controller.Library;
 14using MediaBrowser.Controller.Providers;
 15using MediaBrowser.Controller.Resolvers;
 16using MediaBrowser.Model.IO;
 17using Microsoft.Extensions.Logging;
 18
 19namespace Emby.Server.Implementations.Library.Resolvers.Audio
 20{
 21    /// <summary>
 22    /// The music album resolver.
 23    /// </summary>
 24    public class MusicAlbumResolver : ItemResolver<MusicAlbum>
 25    {
 26        private readonly ILogger<MusicAlbumResolver> _logger;
 27        private readonly NamingOptions _namingOptions;
 28        private readonly IDirectoryService _directoryService;
 29
 30        /// <summary>
 31        /// Initializes a new instance of the <see cref="MusicAlbumResolver"/> class.
 32        /// </summary>
 33        /// <param name="logger">The logger.</param>
 34        /// <param name="namingOptions">The naming options.</param>
 35        /// <param name="directoryService">The directory service.</param>
 2236        public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, NamingOptions namingOptions, IDirectoryService dir
 37        {
 2238            _logger = logger;
 2239            _namingOptions = namingOptions;
 2240            _directoryService = directoryService;
 2241        }
 42
 43        /// <summary>
 44        /// Gets the priority.
 45        /// </summary>
 46        /// <value>The priority.</value>
 2247        public override ResolverPriority Priority => ResolverPriority.Third;
 48
 49        /// <summary>
 50        /// Resolves the specified args.
 51        /// </summary>
 52        /// <param name="args">The args.</param>
 53        /// <returns>MusicAlbum.</returns>
 54        protected override MusicAlbum Resolve(ItemResolveArgs args)
 55        {
 356            var collectionType = args.GetCollectionType();
 357            var isMusicMediaFolder = collectionType == CollectionType.music;
 58
 59            // If there's a collection type and it's not music, don't allow it.
 360            if (!isMusicMediaFolder)
 61            {
 362                return null;
 63            }
 64
 065            if (!args.IsDirectory)
 66            {
 067                return null;
 68            }
 69
 70            // Avoid mis-identifying top folders
 071            if (args.HasParent<MusicAlbum>())
 72            {
 073                return null;
 74            }
 75
 076            if (args.Parent.IsRoot)
 77            {
 078                return null;
 79            }
 80
 081            return IsMusicAlbum(args) ? new MusicAlbum() : null;
 82        }
 83
 84        /// <summary>
 85        /// Determine if the supplied file data points to a music album.
 86        /// </summary>
 87        /// <param name="path">The path to check.</param>
 88        /// <param name="directoryService">The directory service.</param>
 89        /// <returns><c>true</c> if the provided path points to a music album; otherwise, <c>false</c>.</returns>
 90        public bool IsMusicAlbum(string path, IDirectoryService directoryService)
 91        {
 092            return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService);
 93        }
 94
 95        /// <summary>
 96        /// Determine if the supplied resolve args should be considered a music album.
 97        /// </summary>
 98        /// <param name="args">The args.</param>
 99        /// <returns><c>true</c> if [is music album] [the specified args]; otherwise, <c>false</c>.</returns>
 100        private bool IsMusicAlbum(ItemResolveArgs args)
 101        {
 0102            if (args.IsDirectory)
 103            {
 104                // If args is a artist subfolder it's not a music album
 0105                foreach (var subfolder in _namingOptions.ArtistSubfolders)
 106                {
 0107                    if (Path.GetDirectoryName(args.Path.AsSpan()).Equals(subfolder, StringComparison.OrdinalIgnoreCase))
 108                    {
 0109                        _logger.LogDebug("Found release folder: {Path}", args.Path);
 0110                        return false;
 111                    }
 112                }
 113
 114                // If args contains music it's a music album
 0115                if (ContainsMusic(args.FileSystemChildren, true, _directoryService))
 116                {
 0117                    return true;
 118                }
 119            }
 120
 0121            return false;
 122        }
 123
 124        /// <summary>
 125        /// Determine if the supplied list contains what we should consider music.
 126        /// </summary>
 127        /// <returns><c>true</c> if the provided path list contains music; otherwise, <c>false</c>.</returns>
 128        private bool ContainsMusic(
 129            ICollection<FileSystemMetadata> list,
 130            bool allowSubfolders,
 131            IDirectoryService directoryService)
 132        {
 133            // Check for audio files before digging down into directories
 0134            var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && AudioFileParser.IsAudioFile(f
 0135            if (foundAudioFile)
 136            {
 137                // At least one audio file exists
 0138                return true;
 139            }
 140
 0141            if (!allowSubfolders)
 142            {
 143                // Not music since no audio file exists and we're not looking into subfolders
 0144                return false;
 145            }
 146
 0147            var discSubfolderCount = 0;
 148
 0149            var parser = new AlbumParser(_namingOptions);
 150
 0151            var directories = list.Where(fileSystemInfo => fileSystemInfo.IsDirectory);
 152
 0153            var result = Parallel.ForEach(directories, (fileSystemInfo, state) =>
 0154            {
 0155                var path = fileSystemInfo.FullName;
 0156                var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService);
 0157
 0158                if (hasMusic)
 0159                {
 0160                    if (parser.IsMultiPart(path))
 0161                    {
 0162                        _logger.LogDebug("Found multi-disc folder: {Path}", path);
 0163                        Interlocked.Increment(ref discSubfolderCount);
 0164                    }
 0165                    else
 0166                    {
 0167                        // If there are folders underneath with music that are not multidisc, then this can't be a multi
 0168                        state.Stop();
 0169                    }
 0170                }
 0171            });
 172
 0173            if (!result.IsCompleted)
 174            {
 0175                return false;
 176            }
 177
 0178            return discSubfolderCount > 0;
 179        }
 180    }
 181}