#nullable disable #pragma warning disable CS1591 using System; using System.Collections.Generic; using System.IO; using System.Linq; using Emby.Naming.Audio; using Emby.Naming.AudioBook; using Emby.Naming.Common; using Emby.Naming.Video; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; namespace Emby.Server.Implementations.Library.Resolvers.Audio { /// /// Class AudioResolver. /// public class AudioResolver : ItemResolver, IMultiItemResolver { private readonly NamingOptions _namingOptions; public AudioResolver(NamingOptions namingOptions) { _namingOptions = namingOptions; } /// /// Gets the priority. /// /// The priority. public override ResolverPriority Priority => ResolverPriority.Fifth; public MultiItemResolverResult ResolveMultiple( Folder parent, List files, string collectionType, IDirectoryService directoryService) { var result = ResolveMultipleInternal(parent, files, collectionType); if (result != null) { foreach (var item in result.Items) { SetInitialItemValues((MediaBrowser.Controller.Entities.Audio.Audio)item, null); } } return result; } private MultiItemResolverResult ResolveMultipleInternal( Folder parent, List files, string collectionType) { if (string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase)) { return ResolveMultipleAudio(parent, files, true); } return null; } /// /// Resolves the specified args. /// /// The args. /// Entities.Audio.Audio. protected override MediaBrowser.Controller.Entities.Audio.Audio Resolve(ItemResolveArgs args) { // Return audio if the path is a file and has a matching extension var collectionType = args.GetCollectionType(); var isBooksCollectionType = string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase); if (args.IsDirectory) { if (!isBooksCollectionType) { return null; } return FindAudioBook(args, false); } if (AudioFileParser.IsAudioFile(args.Path, _namingOptions)) { var extension = Path.GetExtension(args.Path); if (string.Equals(extension, ".cue", StringComparison.OrdinalIgnoreCase)) { // if audio file exists of same name, return null return null; } var isMixedCollectionType = string.IsNullOrEmpty(collectionType); // For conflicting extensions, give priority to videos if (isMixedCollectionType && VideoResolver.IsVideoFile(args.Path, _namingOptions)) { return null; } MediaBrowser.Controller.Entities.Audio.Audio item = null; var isMusicCollectionType = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase); // Use regular audio type for mixed libraries, owned items and music if (isMixedCollectionType || args.Parent is null || isMusicCollectionType) { item = new MediaBrowser.Controller.Entities.Audio.Audio(); } else if (isBooksCollectionType) { item = new AudioBook(); } if (item != null) { item.IsShortcut = string.Equals(extension, ".strm", StringComparison.OrdinalIgnoreCase); item.IsInMixedFolder = true; } return item; } return null; } private AudioBook FindAudioBook(ItemResolveArgs args, bool parseName) { // TODO: Allow GetMultiDiscMovie in here var result = ResolveMultipleAudio(args.Parent, args.GetActualFileSystemChildren(), parseName); if (result is null || result.Items.Count != 1 || result.Items[0] is not AudioBook item) { return null; } // If we were supporting this we'd be checking filesFromOtherItems item.IsInMixedFolder = false; item.Name = Path.GetFileName(item.ContainingFolderPath); return item; } private MultiItemResolverResult ResolveMultipleAudio(Folder parent, IEnumerable fileSystemEntries, bool parseName) { var files = new List(); var items = new List(); var leftOver = new List(); // Loop through each child file/folder and see if we find a video foreach (var child in fileSystemEntries) { if (child.IsDirectory) { leftOver.Add(child); } else { files.Add(child); } } var resolver = new AudioBookListResolver(_namingOptions); var resolverResult = resolver.Resolve(files).ToList(); var result = new MultiItemResolverResult { ExtraFiles = leftOver, Items = items }; var isInMixedFolder = resolverResult.Count > 1 || (parent != null && parent.IsTopParent); foreach (var resolvedItem in resolverResult) { if (resolvedItem.Files.Count > 1) { // For now, until we sort out naming for multi-part books continue; } if (resolvedItem.Files.Count == 0) { continue; } var firstMedia = resolvedItem.Files[0]; var libraryItem = new AudioBook { Path = firstMedia.Path, IsInMixedFolder = isInMixedFolder, ProductionYear = resolvedItem.Year, Name = parseName ? resolvedItem.Name : Path.GetFileNameWithoutExtension(firstMedia.Path), // AdditionalParts = resolvedItem.Files.Skip(1).Select(i => i.Path).ToArray(), // LocalAlternateVersions = resolvedItem.AlternateVersions.Select(i => i.Path).ToArray() }; result.Items.Add(libraryItem); } result.ExtraFiles.AddRange(files.Where(i => !ContainsFile(resolverResult, i))); return result; } private static bool ContainsFile(IEnumerable result, FileSystemMetadata file) { return result.Any(i => ContainsFile(i, file)); } private static bool ContainsFile(AudioBookInfo result, FileSystemMetadata file) { return result.Files.Any(i => ContainsFile(i, file)) || result.AlternateVersions.Any(i => ContainsFile(i, file)) || result.Extras.Any(i => ContainsFile(i, file)); } private static bool ContainsFile(AudioBookFileInfo result, FileSystemMetadata file) { return string.Equals(result.Path, file.FullName, StringComparison.OrdinalIgnoreCase); } } }