diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 4d959905d9..5f7a15ea5d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -673,6 +673,8 @@ namespace Emby.Server.Implementations _pluginManager.CreatePlugins(); + Resolve().AddScanners(GetExports()); + Resolve().AddParts( GetExports(), GetExports(), diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index c8026960df..ad59b58ed6 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -230,6 +230,8 @@ namespace Emby.Server.Implementations.Library private IMultiItemResolver[] MultiItemResolvers { get; set; } = []; + private IScanner[] Scanners { get; set; } = Array.Empty(); + /// /// Gets or sets the comparers. /// @@ -261,6 +263,16 @@ namespace Emby.Server.Implementations.Library PostscanTasks = postscanTasks.ToArray(); } + public void AddScanners(IEnumerable scanners) + { + Scanners = scanners.ToArray(); + } + + public bool SupportsScanner(IScanner scanner) + { + return Scanners.Select(s => s.Enabled && s == scanner).FirstOrDefault(e => false); + } + /// /// Records the configuration values. /// diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index bde1758e99..ca3af2d327 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -826,6 +826,15 @@ public class LibraryController : BaseJellyfinApiController .OrderBy(i => typesList.IndexOf(i.ItemType)) .ToList(); + result.MetadataResolvers = plugins + .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataResolver)) + .Select(i => new LibraryOptionInfoDto + { + Name = i.Name + }) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) + .ToArray(); + result.MetadataSavers = plugins .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataSaver)) .Select(i => new LibraryOptionInfoDto @@ -885,6 +894,16 @@ public class LibraryController : BaseJellyfinApiController typeOptions.Add(new LibraryTypeOptionsDto { Type = type, + MetadataResolvers = plugins + .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) + .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataResolver)) + .Select(i => new LibraryOptionInfoDto + { + Name = i.Name, + DefaultEnabled = false // TODO(Malik): implement IsDefault check + }) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) + .ToArray(), MetadataFetchers = plugins .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) diff --git a/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs b/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs index c492436689..ed69b97923 100644 --- a/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs +++ b/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs @@ -8,6 +8,11 @@ namespace Jellyfin.Api.Models.LibraryDtos; /// public class LibraryOptionsResultDto { + /// + /// Gets or sets the metadata readers. + /// + public IReadOnlyList MetadataResolvers { get; set; } = Array.Empty(); + /// /// Gets or sets the metadata savers. /// diff --git a/Jellyfin.Api/Models/LibraryDtos/LibraryTypeOptionsDto.cs b/Jellyfin.Api/Models/LibraryDtos/LibraryTypeOptionsDto.cs index f76c4a9678..6d0a477436 100644 --- a/Jellyfin.Api/Models/LibraryDtos/LibraryTypeOptionsDto.cs +++ b/Jellyfin.Api/Models/LibraryDtos/LibraryTypeOptionsDto.cs @@ -15,6 +15,11 @@ public class LibraryTypeOptionsDto /// public string? Type { get; set; } + /// + /// Gets or sets the metadata fetchers. + /// + public IReadOnlyList MetadataResolvers { get; set; } = Array.Empty(); + /// /// Gets or sets the metadata fetchers. /// diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index df90f546cd..553040df0b 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -213,6 +213,8 @@ namespace MediaBrowser.Controller.Library /// IEnumerable{System.String}. Task> GetIntros(BaseItem item, User user); + public bool SupportsScanner(IScanner scanner); + /// /// Adds the parts. /// @@ -228,6 +230,8 @@ namespace MediaBrowser.Controller.Library IEnumerable itemComparers, IEnumerable postscanTasks); + void AddScanners(IEnumerable scanners); + /// /// Sorts the specified items. /// diff --git a/MediaBrowser.Controller/Resolvers/IScanner.cs b/MediaBrowser.Controller/Resolvers/IScanner.cs new file mode 100644 index 0000000000..fbfe120b66 --- /dev/null +++ b/MediaBrowser.Controller/Resolvers/IScanner.cs @@ -0,0 +1,22 @@ +#pragma warning disable CS1591 + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Jellyfin.Data.Enums; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.LiveTv; + +namespace MediaBrowser.Controller.Resolvers +{ + public interface IScanner + { + bool Enabled { get; set; } + + bool Default { get; set; } + + public ICollection ApplyMetadata(ICollection ts); + } +} diff --git a/MediaBrowser.Controller/Resolvers/Scanner.cs b/MediaBrowser.Controller/Resolvers/Scanner.cs new file mode 100644 index 0000000000..46cda5dda6 --- /dev/null +++ b/MediaBrowser.Controller/Resolvers/Scanner.cs @@ -0,0 +1,38 @@ +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Jellyfin.Data.Enums; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.IO; + +namespace MediaBrowser.Controller.Resolvers +{ + public abstract class Scanner : IScanner + { + public bool Enabled { get; set; } = false; + + public bool Default { get; set; } = false; + + public ResolverPriority Priority => ResolverPriority.Plugin; + + protected BaseItem ApplyMetadata(BaseItem t) + { + protected abstract BaseItem ApplyMetadata(BaseItem t); + } + + public ICollection ApplyMetadata(ICollection ts) + { + foreach (var t in ts) + { + ApplyMetadata(t); + } + + return ts; + } + } +} diff --git a/MediaBrowser.Model/Configuration/MetadataOptions.cs b/MediaBrowser.Model/Configuration/MetadataOptions.cs index 384a7997fd..af9964907c 100644 --- a/MediaBrowser.Model/Configuration/MetadataOptions.cs +++ b/MediaBrowser.Model/Configuration/MetadataOptions.cs @@ -18,6 +18,8 @@ namespace MediaBrowser.Model.Configuration MetadataFetcherOrder = Array.Empty(); DisabledImageFetchers = Array.Empty(); ImageFetcherOrder = Array.Empty(); + MetadataResolvers = Array.Empty(); + DisabledMetadataResolvers = Array.Empty(); } public string ItemType { get; set; } @@ -33,5 +35,9 @@ namespace MediaBrowser.Model.Configuration public string[] DisabledImageFetchers { get; set; } public string[] ImageFetcherOrder { get; set; } + + public string[] MetadataResolvers { get; set; } + + public string[] DisabledMetadataResolvers { get; set; } } } diff --git a/MediaBrowser.Model/Configuration/MetadataPluginType.cs b/MediaBrowser.Model/Configuration/MetadataPluginType.cs index 670d6e3837..1591ea8d94 100644 --- a/MediaBrowser.Model/Configuration/MetadataPluginType.cs +++ b/MediaBrowser.Model/Configuration/MetadataPluginType.cs @@ -15,6 +15,7 @@ namespace MediaBrowser.Model.Configuration MetadataSaver, SubtitleFetcher, LyricFetcher, - MediaSegmentProvider + MediaSegmentProvider, + MetadataResolver } } diff --git a/MediaBrowser.Model/Configuration/TypeOptions.cs b/MediaBrowser.Model/Configuration/TypeOptions.cs index d0179e5aab..11ed951634 100644 --- a/MediaBrowser.Model/Configuration/TypeOptions.cs +++ b/MediaBrowser.Model/Configuration/TypeOptions.cs @@ -309,6 +309,8 @@ namespace MediaBrowser.Model.Configuration ImageFetchers = Array.Empty(); ImageFetcherOrder = Array.Empty(); ImageOptions = Array.Empty(); + MetadataResolvers = Array.Empty(); + MetadataResolverOrder = Array.Empty(); } public string Type { get; set; } @@ -321,6 +323,10 @@ namespace MediaBrowser.Model.Configuration public string[] ImageFetcherOrder { get; set; } + public string[] MetadataResolvers { get; set; } + + public string[] MetadataResolverOrder { get; set; } + public ImageOption[] ImageOptions { get; set; } public ImageOption GetImageOptions(ImageType type) diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 856f33b497..e7bd0b9113 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -591,6 +591,14 @@ namespace MediaBrowser.Providers.Manager Type = MetadataPluginType.MediaSegmentProvider })); + // Filesystem to metadata resolvers + var metadataResolverProviders = summary.Plugins.Select(p => p.Type == MetadataPluginType.MetadataResolver && p.Name != null ? p : null); + pluginList.AddRange(metadataResolverProviders.Select(i => new MetadataPlugin + { + Name = i?.Name, + Type = MetadataPluginType.MetadataResolver + })); + summary.Plugins = pluginList.ToArray(); var supportedImageTypes = imageProviders.OfType() @@ -611,10 +619,10 @@ namespace MediaBrowser.Providers.Manager var providers = GetMetadataProvidersInternal(item, libraryOptions, options, true, true).ToList(); // Locals - list.AddRange(providers.Where(i => i is ILocalMetadataProvider).Select(i => new MetadataPlugin + list.AddRange(providers.Where(i => i is IMetadataProvider && i is not IRemoteMetadataProvider && i is not ILocalMetadataProvider && i is not ICustomMetadataProvider).Select(i => new MetadataPlugin { Name = i.Name, - Type = MetadataPluginType.LocalMetadataProvider + Type = MetadataPluginType.MetadataResolver })); // Fetchers