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 { /// /// Class used to resolve Name, Year, alternative files and extras from stack of files. /// public class AudioBookListResolver { private readonly NamingOptions _options; private readonly AudioBookResolver _audioBookResolver; /// /// Initializes a new instance of the class. /// /// Naming options passed along to and . public AudioBookListResolver(NamingOptions options) { _options = options; _audioBookResolver = new AudioBookResolver(_options); } /// /// Resolves Name, Year and differentiate alternative files and extras from regular audiobook files. /// /// List of files related to audiobook. /// Returns IEnumerable of . public IEnumerable Resolve(IEnumerable files) { // File with empty fullname will be sorted out here. var audiobookFileInfos = files .Select(i => _audioBookResolver.Resolve(i.FullName)) .OfType(); var stackResult = StackResolver.ResolveAudioBooks(audiobookFileInfos); foreach (var stack in stackResult) { var stackFiles = stack.Files .Select(i => _audioBookResolver.Resolve(i)) .OfType() .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 stackFiles, out List extras, out List alternativeVersions, AudioBookNameParserResult nameParserResult) { extras = new List(); alternativeVersions = new List(); var haveChaptersOrPages = stackFiles.Any(x => x.ChapterNumber != null || x.PartNumber != 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(); var alt = new List(); 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 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; } } }