diff options
17 files changed, 185 insertions, 213 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c17d355e5..99ad9fdf4 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -19,6 +19,7 @@ using Emby.Dlna; using Emby.Dlna.Main; using Emby.Dlna.Ssdp; using Emby.Drawing; +using Emby.Naming.Common; using Emby.Notifications; using Emby.Photos; using Emby.Server.Implementations.Archiving; @@ -596,6 +597,7 @@ namespace Emby.Server.Implementations serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>)); serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>)); serviceCollection.AddSingleton<ILibraryManager, LibraryManager>(); + serviceCollection.AddSingleton<NamingOptions>(); serviceCollection.AddSingleton<IMusicManager, MusicManager>(); diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index bc5b4499f..29758a078 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -1,8 +1,9 @@ using System; using System.IO; +using Emby.Naming.Audio; +using Emby.Naming.Common; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.IO; @@ -13,17 +14,17 @@ namespace Emby.Server.Implementations.Library /// </summary> public class CoreResolutionIgnoreRule : IResolverIgnoreRule { - private readonly ILibraryManager _libraryManager; + private readonly NamingOptions _namingOptions; private readonly IServerApplicationPaths _serverApplicationPaths; /// <summary> /// Initializes a new instance of the <see cref="CoreResolutionIgnoreRule"/> class. /// </summary> - /// <param name="libraryManager">The library manager.</param> + /// <param name="namingOptions">The naming options.</param> /// <param name="serverApplicationPaths">The server application paths.</param> - public CoreResolutionIgnoreRule(ILibraryManager libraryManager, IServerApplicationPaths serverApplicationPaths) + public CoreResolutionIgnoreRule(NamingOptions namingOptions, IServerApplicationPaths serverApplicationPaths) { - _libraryManager = libraryManager; + _namingOptions = namingOptions; _serverApplicationPaths = serverApplicationPaths; } @@ -78,7 +79,7 @@ namespace Emby.Server.Implementations.Library { // Don't resolve these into audio files if (Path.GetFileNameWithoutExtension(filename.AsSpan()).Equals(BaseItem.ThemeSongFileName, StringComparison.Ordinal) - && _libraryManager.IsAudioFile(filename)) + && AudioFileParser.IsAudioFile(filename, _namingOptions)) { return true; } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 559da7f5c..778b6225e 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -79,6 +79,7 @@ namespace Emby.Server.Implementations.Library private readonly IFileSystem _fileSystem; private readonly IItemRepository _itemRepository; private readonly IImageProcessor _imageProcessor; + private readonly NamingOptions _namingOptions; /// <summary> /// The _root folder sync lock. @@ -88,9 +89,6 @@ namespace Emby.Server.Implementations.Library private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24); - private NamingOptions _namingOptions; - private string[] _videoFileExtensions; - /// <summary> /// The _root folder. /// </summary> @@ -116,6 +114,7 @@ namespace Emby.Server.Implementations.Library /// <param name="itemRepository">The item repository.</param> /// <param name="imageProcessor">The image processor.</param> /// <param name="memoryCache">The memory cache.</param> + /// <param name="namingOptions">The naming options.</param> public LibraryManager( IServerApplicationHost appHost, ILogger<LibraryManager> logger, @@ -130,7 +129,8 @@ namespace Emby.Server.Implementations.Library IMediaEncoder mediaEncoder, IItemRepository itemRepository, IImageProcessor imageProcessor, - IMemoryCache memoryCache) + IMemoryCache memoryCache, + NamingOptions namingOptions) { _appHost = appHost; _logger = logger; @@ -146,6 +146,7 @@ namespace Emby.Server.Implementations.Library _itemRepository = itemRepository; _imageProcessor = imageProcessor; _memoryCache = memoryCache; + _namingOptions = namingOptions; _configurationManager.ConfigurationUpdated += ConfigurationUpdated; @@ -2501,16 +2502,6 @@ namespace Emby.Server.Implementations.Library } /// <inheritdoc /> - public bool IsVideoFile(string path) - { - return VideoResolver.IsVideoFile(path, GetNamingOptions()); - } - - /// <inheritdoc /> - public bool IsAudioFile(string path) - => AudioFileParser.IsAudioFile(path, GetNamingOptions()); - - /// <inheritdoc /> public int? GetSeasonNumberFromPath(string path) => SeasonPathParser.Parse(path, true, true).SeasonNumber; @@ -2525,7 +2516,7 @@ namespace Emby.Server.Implementations.Library isAbsoluteNaming = null; } - var resolver = new EpisodeResolver(GetNamingOptions()); + var resolver = new EpisodeResolver(_namingOptions); var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd; @@ -2682,21 +2673,9 @@ namespace Emby.Server.Implementations.Library return changed; } - /// <inheritdoc /> - public NamingOptions GetNamingOptions() - { - if (_namingOptions == null) - { - _namingOptions = new NamingOptions(); - _videoFileExtensions = _namingOptions.VideoFileExtensions; - } - - return _namingOptions; - } - public ItemLookupInfo ParseName(string name) { - var namingOptions = GetNamingOptions(); + var namingOptions = _namingOptions; var result = VideoResolver.CleanDateTime(name, namingOptions); return new ItemLookupInfo @@ -2708,11 +2687,11 @@ namespace Emby.Server.Implementations.Library public IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService) { - var namingOptions = GetNamingOptions(); + var namingOptions = _namingOptions; var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory) .Where(i => string.Equals(i.Name, BaseItem.TrailersFolderName, StringComparison.OrdinalIgnoreCase)) - .SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false)) + .SelectMany(i => _fileSystem.GetFiles(i.FullName, namingOptions.VideoFileExtensions, false, false)) .ToList(); var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions); @@ -2726,7 +2705,7 @@ namespace Emby.Server.Implementations.Library var resolvers = new IItemResolver[] { - new GenericVideoResolver<Trailer>(this) + new GenericVideoResolver<Trailer>(_namingOptions) }; return ResolvePaths(files, directoryService, null, new LibraryOptions(), null, resolvers) @@ -2752,11 +2731,11 @@ namespace Emby.Server.Implementations.Library public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService) { - var namingOptions = GetNamingOptions(); + var namingOptions = _namingOptions; var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory) .Where(i => BaseItem.AllExtrasTypesFolderNames.ContainsKey(i.Name ?? string.Empty)) - .SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false)) + .SelectMany(i => _fileSystem.GetFiles(i.FullName, namingOptions.VideoFileExtensions, false, false)) .ToList(); var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions); @@ -2840,7 +2819,7 @@ namespace Emby.Server.Implementations.Library private void SetExtraTypeFromFilename(Video item) { - var resolver = new ExtraResolver(GetNamingOptions()); + var resolver = new ExtraResolver(_namingOptions); var result = resolver.GetExtraInfo(item.Path); diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index fd9747b4b..7a6aea9c1 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -6,7 +6,10 @@ 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; @@ -21,11 +24,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// </summary> public class AudioResolver : ItemResolver<MediaBrowser.Controller.Entities.Audio.Audio>, IMultiItemResolver { - private readonly ILibraryManager _libraryManager; + private readonly NamingOptions _namingOptions; - public AudioResolver(ILibraryManager libraryManager) + public AudioResolver(NamingOptions namingOptions) { - _libraryManager = libraryManager; + _namingOptions = namingOptions; } /// <summary> @@ -40,7 +43,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio string collectionType, IDirectoryService directoryService) { - var result = ResolveMultipleInternal(parent, files, collectionType, directoryService); + var result = ResolveMultipleInternal(parent, files, collectionType); if (result != null) { @@ -56,12 +59,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio private MultiItemResolverResult ResolveMultipleInternal( Folder parent, List<FileSystemMetadata> files, - string collectionType, - IDirectoryService directoryService) + string collectionType) { if (string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase)) { - return ResolveMultipleAudio<AudioBook>(parent, files, directoryService, false, collectionType, true); + return ResolveMultipleAudio(parent, files, true); } return null; @@ -87,14 +89,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio return null; } - var files = args.FileSystemChildren - .Where(i => !_libraryManager.IgnoreFile(i, args.Parent)) - .ToList(); - - return FindAudio<AudioBook>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false); + return FindAudioBook(args, false); } - if (_libraryManager.IsAudioFile(args.Path)) + if (AudioFileParser.IsAudioFile(args.Path, _namingOptions)) { var extension = Path.GetExtension(args.Path); @@ -107,7 +105,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var isMixedCollectionType = string.IsNullOrEmpty(collectionType); // For conflicting extensions, give priority to videos - if (isMixedCollectionType && _libraryManager.IsVideoFile(args.Path)) + if (isMixedCollectionType && VideoResolver.IsVideoFile(args.Path, _namingOptions)) { return null; } @@ -141,29 +139,23 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio return null; } - private T FindAudio<T>(ItemResolveArgs args, string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, string collectionType, bool parseName) - where T : MediaBrowser.Controller.Entities.Audio.Audio, new() + private AudioBook FindAudioBook(ItemResolveArgs args, bool parseName) { // TODO: Allow GetMultiDiscMovie in here - const bool supportsMultiVersion = false; + var result = ResolveMultipleAudio(args.Parent, args.GetActualFileSystemChildren(), parseName); - var result = ResolveMultipleAudio<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ?? - new MultiItemResolverResult(); - - if (result.Items.Count == 1) + if (result == null || result.Items.Count != 1 || result.Items[0] is not AudioBook item) { - // If we were supporting this we'd be checking filesFromOtherItems - var item = (T)result.Items[0]; - item.IsInMixedFolder = false; - item.Name = Path.GetFileName(item.ContainingFolderPath); - return item; + return null; } - 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<T>(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions, string collectionType, bool parseName) - where T : MediaBrowser.Controller.Entities.Audio.Audio, new() + private MultiItemResolverResult ResolveMultipleAudio(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, bool parseName) { var files = new List<FileSystemMetadata>(); var items = new List<BaseItem>(); @@ -176,15 +168,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio { leftOver.Add(child); } - else if (!IsIgnored(child.Name)) + else { files.Add(child); } } - var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); - - var resolver = new AudioBookListResolver(namingOptions); + var resolver = new AudioBookListResolver(_namingOptions); var resolverResult = resolver.Resolve(files).ToList(); var result = new MultiItemResolverResult @@ -210,7 +200,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var firstMedia = resolvedItem.Files[0]; - var libraryItem = new T + var libraryItem = new AudioBook { Path = firstMedia.Path, IsInMixedFolder = isInMixedFolder, @@ -230,12 +220,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio return result; } - private bool ContainsFile(List<AudioBookInfo> result, FileSystemMetadata file) + private static bool ContainsFile(IEnumerable<AudioBookInfo> result, FileSystemMetadata file) { return result.Any(i => ContainsFile(i, file)); } - private bool ContainsFile(AudioBookInfo result, FileSystemMetadata file) + private static bool ContainsFile(AudioBookInfo result, FileSystemMetadata file) { return result.Files.Any(i => ContainsFile(i, file)) || result.AlternateVersions.Any(i => ContainsFile(i, file)) || @@ -246,10 +236,5 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio { return string.Equals(result.Path, file.FullName, StringComparison.OrdinalIgnoreCase); } - - private static bool IsIgnored(string filename) - { - return false; - } } } diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index 9e3f62276..a9819a364 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Emby.Naming.Audio; +using Emby.Naming.Common; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -22,20 +23,17 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio public class MusicAlbumResolver : ItemResolver<MusicAlbum> { private readonly ILogger<MusicAlbumResolver> _logger; - private readonly IFileSystem _fileSystem; - private readonly ILibraryManager _libraryManager; + private readonly NamingOptions _namingOptions; /// <summary> /// Initializes a new instance of the <see cref="MusicAlbumResolver"/> class. /// </summary> /// <param name="logger">The logger.</param> - /// <param name="fileSystem">The file system.</param> - /// <param name="libraryManager">The library manager.</param> - public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, IFileSystem fileSystem, ILibraryManager libraryManager) + /// <param name="namingOptions">The naming options.</param> + public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, NamingOptions namingOptions) { _logger = logger; - _fileSystem = fileSystem; - _libraryManager = libraryManager; + _namingOptions = namingOptions; } /// <summary> @@ -87,7 +85,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// <returns><c>true</c> if the provided path points to a music album, <c>false</c> otherwise.</returns> public bool IsMusicAlbum(string path, IDirectoryService directoryService) { - return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, _libraryManager); + return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService); } /// <summary> @@ -101,7 +99,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio if (args.IsDirectory) { // if (args.Parent is MusicArtist) return true; // saves us from testing children twice - if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, _libraryManager)) + if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService)) { return true; } @@ -116,13 +114,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio private bool ContainsMusic( IEnumerable<FileSystemMetadata> list, bool allowSubfolders, - IDirectoryService directoryService, - ILogger<MusicAlbumResolver> logger, - IFileSystem fileSystem, - ILibraryManager libraryManager) + IDirectoryService directoryService) { // check for audio files before digging down into directories - var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && libraryManager.IsAudioFile(fileSystemInfo.FullName)); + var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && AudioFileParser.IsAudioFile(fileSystemInfo.FullName, _namingOptions)); if (foundAudioFile) { // at least one audio file exists @@ -137,21 +132,20 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var discSubfolderCount = 0; - var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); - var parser = new AlbumParser(namingOptions); + var parser = new AlbumParser(_namingOptions); var directories = list.Where(fileSystemInfo => fileSystemInfo.IsDirectory); var result = Parallel.ForEach(directories, (fileSystemInfo, state) => { var path = fileSystemInfo.FullName; - var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager); + var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService); if (hasMusic) { if (parser.IsMultiPart(path)) { - logger.LogDebug("Found multi-disc folder: {Path}", path); + _logger.LogDebug("Found multi-disc folder: {Path}", path); Interlocked.Increment(ref discSubfolderCount); } else diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs index 3d2ae95d2..27e18be42 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using Emby.Naming.Common; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; @@ -19,27 +20,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio public class MusicArtistResolver : ItemResolver<MusicArtist> { private readonly ILogger<MusicAlbumResolver> _logger; - private readonly IFileSystem _fileSystem; - private readonly ILibraryManager _libraryManager; - private readonly IServerConfigurationManager _config; + private NamingOptions _namingOptions; /// <summary> /// Initializes a new instance of the <see cref="MusicArtistResolver"/> class. /// </summary> /// <param name="logger">The logger for the created <see cref="MusicAlbumResolver"/> instances.</param> - /// <param name="fileSystem">The file system.</param> - /// <param name="libraryManager">The library manager.</param> - /// <param name="config">The configuration manager.</param> + /// <param name="namingOptions">The naming options.</param> public MusicArtistResolver( ILogger<MusicAlbumResolver> logger, - IFileSystem fileSystem, - ILibraryManager libraryManager, - IServerConfigurationManager config) + NamingOptions namingOptions) { _logger = logger; - _fileSystem = fileSystem; - _libraryManager = libraryManager; - _config = config; + _namingOptions = namingOptions; } /// <summary> @@ -89,7 +82,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var directoryService = args.DirectoryService; - var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager); + var albumResolver = new MusicAlbumResolver(_logger, _namingOptions); // If we contain an album assume we are an artist folder var directories = args.FileSystemChildren.Where(i => i.IsDirectory); diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index 9ff99fa43..0ebf0e530 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -6,6 +6,7 @@ using System; using System.IO; using System.Linq; using DiscUtils.Udf; +using Emby.Naming.Common; using Emby.Naming.Video; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -21,12 +22,12 @@ namespace Emby.Server.Implementations.Library.Resolvers public abstract class BaseVideoResolver<T> : MediaBrowser.Controller.Resolvers.ItemResolver<T> where T : Video, new() { - protected BaseVideoResolver(ILibraryManager libraryManager) + protected BaseVideoResolver(NamingOptions namingOptions) { - LibraryManager = libraryManager; + NamingOptions = namingOptions; } - protected ILibraryManager LibraryManager { get; } + protected NamingOptions NamingOptions { get; } /// <summary> /// Resolves the specified args. @@ -48,7 +49,7 @@ namespace Emby.Server.Implementations.Library.Resolvers protected virtual TVideoType ResolveVideo<TVideoType>(ItemResolveArgs args, bool parseName) where TVideoType : Video, new() { - var namingOptions = LibraryManager.GetNamingOptions(); + var namingOptions = NamingOptions; // If the path is a file check for a matching extensions if (args.IsDirectory) @@ -138,7 +139,7 @@ namespace Emby.Server.Implementations.Library.Resolvers return null; } - if (LibraryManager.IsVideoFile(args.Path) || videoInfo.IsStub) + if (VideoResolver.IsVideoFile(args.Path, NamingOptions) || videoInfo.IsStub) { var path = args.Path; @@ -267,7 +268,7 @@ namespace Emby.Server.Implementations.Library.Resolvers protected void Set3DFormat(Video video) { - var result = Format3DParser.Parse(video.Path, LibraryManager.GetNamingOptions()); + var result = Format3DParser.Parse(video.Path, NamingOptions); Set3DFormat(video, result.Is3D, result.Format3D); } diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs index 9599faea4..72341d9db 100644 --- a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs @@ -2,16 +2,16 @@ #pragma warning disable CS1591 +using Emby.Naming.Common; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; namespace Emby.Server.Implementations.Library.Resolvers { public class GenericVideoResolver<T> : BaseVideoResolver<T> where T : Video, new() { - public GenericVideoResolver(ILibraryManager libraryManager) - : base(libraryManager) + public GenericVideoResolver(NamingOptions namingOptions) + : base(namingOptions) { } } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index f3b6ef0a2..732be0fe5 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; +using Emby.Naming.Common; using Emby.Naming.Video; using Jellyfin.Extensions; using MediaBrowser.Controller.Drawing; @@ -25,6 +26,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies public class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver { private readonly IImageProcessor _imageProcessor; + private readonly StackResolver _stackResolver; private string[] _validCollectionTypes = new[] { @@ -38,12 +40,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies /// <summary> /// Initializes a new instance of the <see cref="MovieResolver"/> class. /// </summary> - /// <param name="libraryManager">The library manager.</param> /// <param name="imageProcessor">The image processor.</param> - public MovieResolver(ILibraryManager libraryManager, IImageProcessor imageProcessor) - : base(libraryManager) + /// <param name="namingOptions">The naming options.</param> + public MovieResolver(IImageProcessor imageProcessor, NamingOptions namingOptions) + : base(namingOptions) { _imageProcessor = imageProcessor; + _stackResolver = new StackResolver(NamingOptions); } /// <summary> @@ -89,9 +92,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return null; } - var files = args.FileSystemChildren - .Where(i => !LibraryManager.IgnoreFile(i, args.Parent)) - .ToList(); + var files = args.GetActualFileSystemChildren().ToList(); if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)) { @@ -258,9 +259,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies } } - var namingOptions = LibraryManager.GetNamingOptions(); - - var resolverResult = VideoListResolver.Resolve(files, namingOptions, suppportMultiEditions).ToList(); + var resolverResult = VideoListResolver.Resolve(files, NamingOptions, suppportMultiEditions).ToList(); var result = new MultiItemResolverResult { @@ -438,7 +437,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies if (result.Items.Count == 1) { var videoPath = result.Items[0].Path; - var hasPhotos = photos.Any(i => !PhotoResolver.IsOwnedByResolvedMedia(LibraryManager, videoPath, i.Name)); + var hasPhotos = photos.Any(i => !PhotoResolver.IsOwnedByResolvedMedia(videoPath, i.Name)); if (!hasPhotos) { @@ -511,9 +510,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return null; } - var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions(); - - var result = new StackResolver(namingOptions).ResolveDirectories(folderPaths).ToList(); + var result = _stackResolver.ResolveDirectories(folderPaths).ToList(); if (result.Count != 1) { diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs index 1c560e8a6..7dd0ab185 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs @@ -1,6 +1,7 @@ #nullable disable using System; +using Emby.Naming.Common; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -15,17 +16,17 @@ namespace Emby.Server.Implementations.Library.Resolvers public class PhotoAlbumResolver : GenericFolderResolver<PhotoAlbum> { private readonly IImageProcessor _imageProcessor; - private readonly ILibraryManager _libraryManager; + private readonly NamingOptions _namingOptions; /// <summary> /// Initializes a new instance of the <see cref="PhotoAlbumResolver"/> class. /// </summary> /// <param name="imageProcessor">The image processor.</param> - /// <param name="libraryManager">The library manager.</param> - public PhotoAlbumResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager) + /// <param name="namingOptions">The naming options.</param> + public PhotoAlbumResolver(IImageProcessor imageProcessor, NamingOptions namingOptions) { _imageProcessor = imageProcessor; - _libraryManager = libraryManager; + _namingOptions = namingOptions; } /// <inheritdoc /> @@ -73,7 +74,7 @@ namespace Emby.Server.Implementations.Library.Resolvers foreach (var siblingFile in files) { - if (PhotoResolver.IsOwnedByMedia(_libraryManager, siblingFile.FullName, filename)) + if (PhotoResolver.IsOwnedByMedia(_namingOptions, siblingFile.FullName, filename)) { ownedByMedia = true; break; diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs index 57bf40e9e..51d819303 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -6,6 +6,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using Emby.Naming.Common; +using Emby.Naming.Video; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -16,7 +18,8 @@ namespace Emby.Server.Implementations.Library.Resolvers public class PhotoResolver : ItemResolver<Photo> { private readonly IImageProcessor _imageProcessor; - private readonly ILibraryManager _libraryManager; + private readonly NamingOptions _namingOptions; + private static readonly HashSet<string> _ignoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "folder", @@ -30,10 +33,11 @@ namespace Emby.Server.Implementations.Library.Resolvers "default" }; - public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager) + + public PhotoResolver(IImageProcessor imageProcessor, NamingOptions namingOptions) { _imageProcessor = imageProcessor; - _libraryManager = libraryManager; + _namingOptions = namingOptions; } /// <summary> @@ -60,7 +64,7 @@ namespace Emby.Server.Implementations.Library.Resolvers foreach (var file in files) { - if (IsOwnedByMedia(_libraryManager, file.FullName, filename)) + if (IsOwnedByMedia(_namingOptions, file.FullName, filename)) { return null; } @@ -77,17 +81,12 @@ namespace Emby.Server.Implementations.Library.Resolvers return null; } - internal static bool IsOwnedByMedia(ILibraryManager libraryManager, string file, string imageFilename) + internal static bool IsOwnedByMedia(NamingOptions namingOptions, string file, string imageFilename) { - if (libraryManager.IsVideoFile(file)) - { - return IsOwnedByResolvedMedia(libraryManager, file, imageFilename); - } - - return false; + return VideoResolver.IsVideoFile(file, namingOptions) && IsOwnedByResolvedMedia(file, imageFilename); } - internal static bool IsOwnedByResolvedMedia(ILibraryManager libraryManager, string file, string imageFilename) + internal static bool IsOwnedByResolvedMedia(string file, string imageFilename) => imageFilename.StartsWith(Path.GetFileNameWithoutExtension(file), StringComparison.OrdinalIgnoreCase); internal static bool IsImageFile(string path, IImageProcessor imageProcessor) diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index cf2d22f4d..f72da3617 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -2,6 +2,7 @@ using System; using System.Linq; +using Emby.Naming.Common; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -17,9 +18,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// <summary> /// Initializes a new instance of the <see cref="EpisodeResolver"/> class. /// </summary> - /// <param name="libraryManager">The library manager.</param> - public EpisodeResolver(ILibraryManager libraryManager) - : base(libraryManager) + /// <param name="namingOptions">The naming options.</param> + public EpisodeResolver(NamingOptions namingOptions) + : base(namingOptions) { } diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs index 063f67543..ea4851458 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs @@ -1,6 +1,7 @@ #nullable disable using System.Globalization; +using Emby.Naming.Common; using Emby.Naming.TV; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -14,22 +15,22 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// </summary> public class SeasonResolver : GenericFolderResolver<Season> { - private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; private readonly ILogger<SeasonResolver> _logger; + private readonly NamingOptions _namingOptions; /// <summary> /// Initializes a new instance of the <see cref="SeasonResolver"/> class. /// </summary> - /// <param name="libraryManager">The library manager.</param> + /// <param name="namingOptions">The naming options.</param> /// <param name="localization">The localization.</param> /// <param name="logger">The logger.</param> public SeasonResolver( - ILibraryManager libraryManager, + NamingOptions namingOptions, ILocalizationManager localization, ILogger<SeasonResolver> logger) { - _libraryManager = libraryManager; + _namingOptions = namingOptions; _localization = localization; _logger = logger; } @@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV { if (args.Parent is Series series && args.IsDirectory) { - var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); + var namingOptions = _namingOptions; var path = args.Path; @@ -65,18 +66,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV var episodeInfo = resolver.Resolve(testPath, true); - if (episodeInfo != null) + if (episodeInfo?.EpisodeNumber != null && episodeInfo.SeasonNumber.HasValue) { - if (episodeInfo.EpisodeNumber.HasValue && episodeInfo.SeasonNumber.HasValue) - { - _logger.LogDebug( - "Found folder underneath series with episode number: {0}. Season {1}. Episode {2}", - path, - episodeInfo.SeasonNumber.Value, - episodeInfo.EpisodeNumber.Value); - - return null; - } + _logger.LogDebug( + "Found folder underneath series with episode number: {0}. Season {1}. Episode {2}", + path, + episodeInfo.SeasonNumber.Value, + episodeInfo.EpisodeNumber.Value); + + return null; } } diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index e62890083..6c04ecff0 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -5,7 +5,12 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using Emby.Naming.Common; using Emby.Naming.TV; +using Emby.Naming.Video; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; @@ -21,17 +26,23 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV public class SeriesResolver : GenericFolderResolver<Series> { private readonly ILogger<SeriesResolver> _logger; - private readonly ILibraryManager _libraryManager; + private readonly NamingOptions _namingOptions; + private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _configurationManager; /// <summary> /// Initializes a new instance of the <see cref="SeriesResolver"/> class. /// </summary> /// <param name="logger">The logger.</param> - /// <param name="libraryManager">The library manager.</param> - public SeriesResolver(ILogger<SeriesResolver> logger, ILibraryManager libraryManager) + /// <param name="namingOptions">The naming options.</param> + /// <param name="fileSystem">The file system.</param> + /// <param name="configurationManager">The server configuration manager.</param> + public SeriesResolver(ILogger<SeriesResolver> logger, NamingOptions namingOptions, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { _logger = logger; - _libraryManager = libraryManager; + _namingOptions = namingOptions; + _fileSystem = fileSystem; + _configurationManager = configurationManager; } /// <summary> @@ -54,12 +65,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV return null; } - var seriesInfo = Naming.TV.SeriesResolver.Resolve(_libraryManager.GetNamingOptions(), args.Path); + var seriesInfo = Naming.TV.SeriesResolver.Resolve(_namingOptions, args.Path); var collectionType = args.GetCollectionType(); if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) { - var configuredContentType = _libraryManager.GetConfiguredContentType(args.Path); + // TODO refactor into separate class or something, this is copied from LibraryManager.GetConfiguredContentType + var configuredContentType = args.GetConfiguredContentType(); if (!string.Equals(configuredContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) { return new Series @@ -91,7 +103,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV return null; } - if (IsSeriesFolder(args.Path, args.FileSystemChildren, _logger, _libraryManager, false)) + if (IsSeriesFolder(args.Path, args.FileSystemChildren, false)) { return new Series { @@ -105,11 +117,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV return null; } - public static bool IsSeriesFolder( + private bool IsSeriesFolder( string path, IEnumerable<FileSystemMetadata> fileSystemChildren, - ILogger<SeriesResolver> logger, - ILibraryManager libraryManager, bool isTvContentType) { foreach (var child in fileSystemChildren) @@ -118,21 +128,21 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV { if (IsSeasonFolder(child.FullName, isTvContentType)) { - logger.LogDebug("{Path} is a series because of season folder {Dir}.", path, child.FullName); + _logger.LogDebug("{Path} is a series because of season folder {Dir}.", path, child.FullName); return true; } } else { string fullName = child.FullName; - if (libraryManager.IsVideoFile(fullName)) + if (VideoResolver.IsVideoFile(path, _namingOptions)) { if (isTvContentType) { return true; } - var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions(); + var namingOptions = _namingOptions; var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions); @@ -145,7 +155,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV } } - logger.LogDebug("{Path} is not a series folder.", path); + _logger.LogDebug("{Path} is not a series folder.", path); return false; } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index d40e56c7d..1e1e2adb8 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -397,20 +397,6 @@ namespace MediaBrowser.Controller.Library string sortName); /// <summary> - /// Determines whether [is video file] [the specified path]. - /// </summary> - /// <param name="path">The path.</param> - /// <returns><c>true</c> if [is video file] [the specified path]; otherwise, <c>false</c>.</returns> - bool IsVideoFile(string path); - - /// <summary> - /// Determines whether [is audio file] [the specified path]. - /// </summary> - /// <param name="path">The path.</param> - /// <returns><c>true</c> if [is audio file] [the specified path]; otherwise, <c>false</c>.</returns> - bool IsAudioFile(string path); - - /// <summary> /// Gets the season number from path. /// </summary> /// <param name="path">The path.</param> @@ -625,11 +611,5 @@ namespace MediaBrowser.Controller.Library BaseItem GetParentItem(string parentId, Guid? userId); BaseItem GetParentItem(Guid? parentId, Guid? userId); - - /// <summary> - /// Gets or creates a static instance of <see cref="NamingOptions"/>. - /// </summary> - /// <returns>An instance of the <see cref="NamingOptions"/> class.</returns> - NamingOptions GetNamingOptions(); } } diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index bfc1e4857..91d162b41 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -36,6 +36,7 @@ namespace MediaBrowser.Controller.Library DirectoryService = directoryService; } + // TODO remove dependencies as properties, they should be injected where it makes sense public IDirectoryService DirectoryService { get; } /// <summary> @@ -237,6 +238,40 @@ namespace MediaBrowser.Controller.Library } /// <summary> + /// Gets the configured content type for the path. + /// </summary> + /// <remarks> + /// This is subject to future refactoring as it relies on a static property in BaseItem. + /// </remarks> + /// <returns>The configured content type.</returns> + public string GetConfiguredContentType() + { + return BaseItem.LibraryManager.GetConfiguredContentType(Path); + } + + /// <summary> + /// Gets the file system children that do not hit the ignore file check. + /// </summary> + /// <remarks> + /// This is subject to future refactoring as it relies on a static property in BaseItem. + /// </remarks> + /// <returns>The file system children that are not ignored.</returns> + public IEnumerable<FileSystemMetadata> GetActualFileSystemChildren() + { + var numberOfChildren = FileSystemChildren.Length; + for (var i = 0; i < numberOfChildren; i++) + { + var child = FileSystemChildren[i]; + if (BaseItem.LibraryManager.IgnoreFile(child, Parent)) + { + continue; + } + + yield return child; + } + } + + /// <summary> /// Returns a hash code for this instance. /// </summary> /// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns> diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs index c393742eb..a0fe4a5cf 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs @@ -1,5 +1,4 @@ -using System; -using Emby.Server.Implementations.Library.Resolvers.TV; +using Emby.Server.Implementations.Library.Resolvers.TV; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; @@ -17,19 +16,16 @@ namespace Jellyfin.Server.Implementations.Tests.Library [Fact] public void Resolve_GivenVideoInExtrasFolder_DoesNotResolveToEpisode() { - var season = new Season { Name = "Season 1" }; var parent = new Folder { Name = "extras" }; - var libraryManagerMock = new Mock<ILibraryManager>(); - libraryManagerMock.Setup(x => x.GetItemById(It.IsAny<Guid>())).Returns(season); - var episodeResolver = new EpisodeResolver(libraryManagerMock.Object); + var episodeResolver = new EpisodeResolver(null); var itemResolveArgs = new ItemResolveArgs( Mock.Of<IServerApplicationPaths>(), Mock.Of<IDirectoryService>()) { Parent = parent, CollectionType = CollectionType.TvShows, - FileInfo = new FileSystemMetadata() + FileInfo = new FileSystemMetadata { FullName = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv" } @@ -45,7 +41,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library // Have to create a mock because of moq proxies not being castable to a concrete implementation // https://github.com/jellyfin/jellyfin/blob/ab0cff8556403e123642dc9717ba778329554634/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs#L48 - var episodeResolver = new EpisodeResolverMock(Mock.Of<ILibraryManager>()); + var episodeResolver = new EpisodeResolverMock(); var itemResolveArgs = new ItemResolveArgs( Mock.Of<IServerApplicationPaths>(), Mock.Of<IDirectoryService>()) @@ -62,7 +58,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library private class EpisodeResolverMock : EpisodeResolver { - public EpisodeResolverMock(ILibraryManager libraryManager) : base(libraryManager) + public EpisodeResolverMock() : base(null) { } |
