< Summary - Jellyfin

Information
Class: Emby.Naming.AudioBook.AudioBookListResolver
Assembly: Emby.Naming
File(s): /srv/git/jellyfin/Emby.Naming/AudioBook/AudioBookListResolver.cs
Line coverage
100%
Covered lines: 51
Uncovered lines: 0
Coverable lines: 51
Total lines: 150
Line coverage: 100%
Branch coverage
100%
Covered branches: 30
Total branches: 30
Branch coverage: 100%
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%
FindExtraAndAlternativeFiles(...)100%2626100%
FindMainAudioBookFile(...)100%44100%

File(s)

/srv/git/jellyfin/Emby.Naming/AudioBook/AudioBookListResolver.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using System.Linq;
 5using Emby.Naming.Common;
 6using Emby.Naming.Video;
 7using MediaBrowser.Model.IO;
 8
 9namespace Emby.Naming.AudioBook
 10{
 11    /// <summary>
 12    /// Class used to resolve Name, Year, alternative files and extras from stack of files.
 13    /// </summary>
 14    public class AudioBookListResolver
 15    {
 16        private readonly NamingOptions _options;
 17        private readonly AudioBookResolver _audioBookResolver;
 18
 19        /// <summary>
 20        /// Initializes a new instance of the <see cref="AudioBookListResolver"/> class.
 21        /// </summary>
 22        /// <param name="options">Naming options passed along to <see cref="AudioBookResolver"/> and <see cref="AudioBoo
 23        public AudioBookListResolver(NamingOptions options)
 24        {
 2525            _options = options;
 2526            _audioBookResolver = new AudioBookResolver(_options);
 2527        }
 28
 29        /// <summary>
 30        /// Resolves Name, Year and differentiate alternative files and extras from regular audiobook files.
 31        /// </summary>
 32        /// <param name="files">List of files related to audiobook.</param>
 33        /// <returns>Returns IEnumerable of <see cref="AudioBookInfo"/>.</returns>
 34        public IEnumerable<AudioBookInfo> Resolve(IEnumerable<FileSystemMetadata> files)
 35        {
 36            // File with empty fullname will be sorted out here.
 37            var audiobookFileInfos = files
 38                .Select(i => _audioBookResolver.Resolve(i.FullName))
 39                .OfType<AudioBookFileInfo>();
 40
 41            var stackResult = StackResolver.ResolveAudioBooks(audiobookFileInfos);
 42
 43            foreach (var stack in stackResult)
 44            {
 45                var stackFiles = stack.Files
 46                    .Select(i => _audioBookResolver.Resolve(i))
 47                    .OfType<AudioBookFileInfo>()
 48                    .ToList();
 49
 50                stackFiles.Sort();
 51
 52                var nameParserResult = new AudioBookNameParser(_options).Parse(stack.Name);
 53
 54                FindExtraAndAlternativeFiles(ref stackFiles, out var extras, out var alternativeVersions, nameParserResu
 55
 56                var info = new AudioBookInfo(
 57                    nameParserResult.Name,
 58                    nameParserResult.Year,
 59                    stackFiles,
 60                    extras,
 61                    alternativeVersions);
 62
 63                yield return info;
 64            }
 65        }
 66
 67        private void FindExtraAndAlternativeFiles(ref List<AudioBookFileInfo> stackFiles, out List<AudioBookFileInfo> ex
 68        {
 3569            extras = new List<AudioBookFileInfo>();
 3570            alternativeVersions = new List<AudioBookFileInfo>();
 71
 3572            var haveChaptersOrPages = stackFiles.Any(x => x.ChapterNumber is not null || x.PartNumber is not null);
 3573            var groupedBy = stackFiles.GroupBy(file => new { file.ChapterNumber, file.PartNumber });
 3574            var nameWithReplacedDots = nameParserResult.Name.Replace(' ', '.');
 75
 16476            foreach (var group in groupedBy)
 77            {
 4778                if (group.Key.ChapterNumber is null && group.Key.PartNumber is null)
 79                {
 2080                    if (group.Count() > 1 || haveChaptersOrPages)
 81                    {
 982                        List<AudioBookFileInfo>? ex = null;
 983                        List<AudioBookFileInfo>? alt = null;
 84
 5085                        foreach (var audioFile in group)
 86                        {
 1687                            var name = Path.GetFileNameWithoutExtension(audioFile.Path.AsSpan());
 1688                            if (name.Equals("audiobook", StringComparison.OrdinalIgnoreCase)
 1689                                || name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase)
 1690                                || name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase))
 91                            {
 792                                (alt ??= new()).Add(audioFile);
 93                            }
 94                            else
 95                            {
 996                                (ex ??= new()).Add(audioFile);
 97                            }
 98                        }
 99
 9100                        if (ex is not null)
 101                        {
 8102                            var extra = ex
 8103                                .OrderBy(x => x.Container)
 8104                                .ThenBy(x => x.Path)
 8105                                .ToList();
 106
 8107                            stackFiles = stackFiles.Except(extra).ToList();
 8108                            extras.AddRange(extra);
 109                        }
 110
 9111                        if (alt is not null)
 112                        {
 5113                            var alternatives = alt
 5114                                .OrderBy(x => x.Container)
 5115                                .ThenBy(x => x.Path)
 5116                                .ToList();
 117
 5118                            var main = FindMainAudioBookFile(alternatives, nameParserResult.Name);
 5119                            alternatives.Remove(main);
 5120                            stackFiles = stackFiles.Except(alternatives).ToList();
 5121                            alternativeVersions.AddRange(alternatives);
 122                        }
 123                    }
 124                }
 27125                else if (group.Count() > 1)
 126                {
 3127                    var alternatives = group
 3128                        .OrderBy(x => x.Container)
 3129                        .ThenBy(x => x.Path)
 3130                        .Skip(1)
 3131                        .ToList();
 132
 3133                    stackFiles = stackFiles.Except(alternatives).ToList();
 3134                    alternativeVersions.AddRange(alternatives);
 135                }
 136            }
 35137        }
 138
 139        private AudioBookFileInfo FindMainAudioBookFile(List<AudioBookFileInfo> files, string name)
 140        {
 5141            var main = files.Find(x => Path.GetFileNameWithoutExtension(x.Path).Equals(name, StringComparison.OrdinalIgn
 5142            main ??= files.FirstOrDefault(x => Path.GetFileNameWithoutExtension(x.Path).Equals("audiobook", StringCompar
 5143            main ??= files.OrderBy(x => x.Container)
 5144                .ThenBy(x => x.Path)
 5145                .First();
 146
 5147            return main;
 148        }
 149    }
 150}