aboutsummaryrefslogtreecommitdiff
path: root/Emby.Naming/AudioBook/AudioBookListResolver.cs
blob: bdae20b6b2ad3d453dc7b3c30aca0c9f91f86fca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Emby.Naming.Common;
using Emby.Naming.Video;
using MediaBrowser.Model.IO;

namespace Emby.Naming.AudioBook
{
    /// <summary>
    /// Class used to resolve Name, Year, alternative files and extras from stack of files.
    /// </summary>
    public class AudioBookListResolver
    {
        private readonly NamingOptions _options;
        private readonly AudioBookResolver _audioBookResolver;

        /// <summary>
        /// Initializes a new instance of the <see cref="AudioBookListResolver"/> class.
        /// </summary>
        /// <param name="options">Naming options passed along to <see cref="AudioBookResolver"/> and <see cref="AudioBookNameParser"/>.</param>
        public AudioBookListResolver(NamingOptions options)
        {
            _options = options;
            _audioBookResolver = new AudioBookResolver(_options);
        }

        /// <summary>
        /// Resolves Name, Year and differentiate alternative files and extras from regular audiobook files.
        /// </summary>
        /// <param name="files">List of files related to audiobook.</param>
        /// <returns>Returns IEnumerable of <see cref="AudioBookInfo"/>.</returns>
        public IEnumerable<AudioBookInfo> Resolve(IEnumerable<FileSystemMetadata> files)
        {
            // File with empty fullname will be sorted out here.
            var audiobookFileInfos = files
                .Select(i => _audioBookResolver.Resolve(i.FullName))
                .OfType<AudioBookFileInfo>();

            var stackResult = StackResolver.ResolveAudioBooks(audiobookFileInfos);

            foreach (var stack in stackResult)
            {
                var stackFiles = stack.Files
                    .Select(i => _audioBookResolver.Resolve(i))
                    .OfType<AudioBookFileInfo>()
                    .ToList();

                stackFiles.Sort();

                var nameParserResult = new AudioBookNameParser(_options).Parse(stack.Name);

                FindExtraAndAlternativeFiles(ref stackFiles, out var extras, out var alternativeVersions, nameParserResult);

                var info = new AudioBookInfo(
                    nameParserResult.Name,
                    nameParserResult.Year,
                    stackFiles,
                    extras,
                    alternativeVersions);

                yield return info;
            }
        }

        private void FindExtraAndAlternativeFiles(ref List<AudioBookFileInfo> stackFiles, out List<AudioBookFileInfo> extras, out List<AudioBookFileInfo> alternativeVersions, AudioBookNameParserResult nameParserResult)
        {
            extras = new List<AudioBookFileInfo>();
            alternativeVersions = new List<AudioBookFileInfo>();

            var haveChaptersOrPages = stackFiles.Any(x => x.ChapterNumber is not null || x.PartNumber is not null);
            var groupedBy = stackFiles.GroupBy(file => new { file.ChapterNumber, file.PartNumber });
            var nameWithReplacedDots = nameParserResult.Name.Replace(' ', '.');

            foreach (var group in groupedBy)
            {
                if (group.Key.ChapterNumber is null && group.Key.PartNumber is null)
                {
                    if (group.Count() > 1 || haveChaptersOrPages)
                    {
                        var ex = new List<AudioBookFileInfo>();
                        var alt = new List<AudioBookFileInfo>();

                        foreach (var audioFile in group)
                        {
                            var name = Path.GetFileNameWithoutExtension(audioFile.Path);
                            if (name.Equals("audiobook", StringComparison.OrdinalIgnoreCase) ||
                                name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase) ||
                                name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase))
                            {
                                alt.Add(audioFile);
                            }
                            else
                            {
                                ex.Add(audioFile);
                            }
                        }

                        if (ex.Count > 0)
                        {
                            var extra = ex
                                .OrderBy(x => x.Container)
                                .ThenBy(x => x.Path)
                                .ToList();

                            stackFiles = stackFiles.Except(extra).ToList();
                            extras.AddRange(extra);
                        }

                        if (alt.Count > 0)
                        {
                            var alternatives = alt
                                .OrderBy(x => x.Container)
                                .ThenBy(x => x.Path)
                                .ToList();

                            var main = FindMainAudioBookFile(alternatives, nameParserResult.Name);
                            alternatives.Remove(main);
                            stackFiles = stackFiles.Except(alternatives).ToList();
                            alternativeVersions.AddRange(alternatives);
                        }
                    }
                }
                else if (group.Count() > 1)
                {
                    var alternatives = group
                        .OrderBy(x => x.Container)
                        .ThenBy(x => x.Path)
                        .Skip(1)
                        .ToList();

                    stackFiles = stackFiles.Except(alternatives).ToList();
                    alternativeVersions.AddRange(alternatives);
                }
            }
        }

        private AudioBookFileInfo FindMainAudioBookFile(List<AudioBookFileInfo> files, string name)
        {
            var main = files.Find(x => Path.GetFileNameWithoutExtension(x.Path).Equals(name, StringComparison.OrdinalIgnoreCase));
            main ??= files.FirstOrDefault(x => Path.GetFileNameWithoutExtension(x.Path).Equals("audiobook", StringComparison.OrdinalIgnoreCase));
            main ??= files.OrderBy(x => x.Container)
                .ThenBy(x => x.Path)
                .First();

            return main;
        }
    }
}