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; /// /// Initializes a new instance of the class. /// /// Naming options passed along to and . public AudioBookListResolver(NamingOptions options) { _options = 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) { var audioBookResolver = new AudioBookResolver(_options); // File with empty fullname will be sorted out here var audiobookFileInfos = files .Select(i => audioBookResolver.Resolve(i.FullName)) .OfType() .ToList(); var stackResult = new StackResolver(_options) .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 }); foreach (var group in groupedBy) { if (group.Key.ChapterNumber == null && group.Key.PartNumber == 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 == "audiobook" || name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase) || name.Contains(nameParserResult.Name.Replace(" ", "."), 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) == name); main ??= files.FirstOrDefault(x => Path.GetFileNameWithoutExtension(x.Path) == "audiobook"); main ??= files.OrderBy(x => x.Container) .ThenBy(x => x.Path) .First(); return main; } } }