diff options
Diffstat (limited to 'MediaBrowser.Providers/Manager/ProviderManager.cs')
| -rw-r--r-- | MediaBrowser.Providers/Manager/ProviderManager.cs | 306 |
1 files changed, 115 insertions, 191 deletions
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 552ded0c4..ac4dc1bc3 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -48,7 +46,7 @@ namespace MediaBrowser.Providers.Manager /// </summary> public class ProviderManager : IProviderManager, IDisposable { - private readonly object _refreshQueueLock = new object(); + private readonly object _refreshQueueLock = new(); private readonly ILogger<ProviderManager> _logger; private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryMonitor _libraryMonitor; @@ -58,11 +56,11 @@ namespace MediaBrowser.Providers.Manager private readonly ISubtitleManager _subtitleManager; private readonly IServerConfigurationManager _configurationManager; private readonly IBaseItemManager _baseItemManager; - private readonly ConcurrentDictionary<Guid, double> _activeRefreshes = new ConcurrentDictionary<Guid, double>(); - private readonly CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); - private readonly SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>> _refreshQueue = - new SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>>(); + private readonly ConcurrentDictionary<Guid, double> _activeRefreshes = new(); + private readonly CancellationTokenSource _disposeCancellationTokenSource = new(); + private readonly SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>> _refreshQueue = new(); + private IImageProvider[] _imageProviders = Array.Empty<IImageProvider>(); private IMetadataService[] _metadataServices = Array.Empty<IMetadataService>(); private IMetadataProvider[] _metadataProviders = Array.Empty<IMetadataProvider>(); private IMetadataSaver[] _savers = Array.Empty<IMetadataSaver>(); @@ -105,15 +103,13 @@ namespace MediaBrowser.Providers.Manager } /// <inheritdoc/> - public event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted; + public event EventHandler<GenericEventArgs<BaseItem>>? RefreshStarted; /// <inheritdoc/> - public event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted; + public event EventHandler<GenericEventArgs<BaseItem>>? RefreshCompleted; /// <inheritdoc/> - public event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress; - - private IImageProvider[] ImageProviders { get; set; } + public event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>>? RefreshProgress; /// <inheritdoc/> public void AddParts( @@ -123,8 +119,7 @@ namespace MediaBrowser.Providers.Manager IEnumerable<IMetadataSaver> metadataSavers, IEnumerable<IExternalId> externalIds) { - ImageProviders = imageProviders.ToArray(); - + _imageProviders = imageProviders.ToArray(); _metadataServices = metadataServices.OrderBy(i => i.Order).ToArray(); _metadataProviders = metadataProviders.ToArray(); _externalIds = externalIds.OrderBy(i => i.ProviderName).ToArray(); @@ -138,26 +133,15 @@ namespace MediaBrowser.Providers.Manager var type = item.GetType(); var service = _metadataServices.FirstOrDefault(current => current.CanRefreshPrimary(type)); + service ??= _metadataServices.FirstOrDefault(current => current.CanRefresh(item)); if (service == null) { - foreach (var current in _metadataServices) - { - if (current.CanRefresh(item)) - { - service = current; - break; - } - } + _logger.LogError("Unable to find a metadata service for item of type {TypeName}", item.GetType().Name); + return Task.FromResult(ItemUpdateType.None); } - if (service != null) - { - return service.RefreshMetadata(item, options, cancellationToken); - } - - _logger.LogError("Unable to find a metadata service for item of type {TypeName}", item.GetType().Name); - return Task.FromResult(ItemUpdateType.None); + return service.RefreshMetadata(item, options, cancellationToken); } /// <inheritdoc/> @@ -181,6 +165,10 @@ namespace MediaBrowser.Providers.Manager { contentType = "image/png"; } + else + { + throw new HttpRequestException("Invalid image received: contentType not set.", null, response.StatusCode); + } } // TVDb will sometimes serve a rubbish 404 html page with a 200 OK code, because reasons... @@ -309,53 +297,69 @@ namespace MediaBrowser.Providers.Manager return GetRemoteImageProviders(item, true).Select(i => new ImageProviderInfo(i.Name, i.GetSupportedImages(item).ToArray())); } - /// <summary> - /// Gets the image providers for the provided item. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="refreshOptions">The image refresh options.</param> - /// <returns>The image providers for the item.</returns> - public IEnumerable<IImageProvider> GetImageProviders(BaseItem item, ImageRefreshOptions refreshOptions) + private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(BaseItem item, bool includeDisabled) { - return GetImageProviders(item, _libraryManager.GetLibraryOptions(item), GetMetadataOptions(item), refreshOptions, false); + var options = GetMetadataOptions(item); + var libraryOptions = _libraryManager.GetLibraryOptions(item); + + return GetImageProvidersInternal( + item, + libraryOptions, + options, + new ImageRefreshOptions(new DirectoryService(_fileSystem)), + includeDisabled).OfType<IRemoteImageProvider>(); } - private IEnumerable<IImageProvider> GetImageProviders(BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled) + /// <inheritdoc/> + public IEnumerable<IImageProvider> GetImageProviders(BaseItem item, ImageRefreshOptions refreshOptions) { - // Avoid implicitly captured closure - var currentOptions = options; + return GetImageProvidersInternal(item, _libraryManager.GetLibraryOptions(item), GetMetadataOptions(item), refreshOptions, false); + } + private IEnumerable<IImageProvider> GetImageProvidersInternal(BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled) + { var typeOptions = libraryOptions.GetTypeOptions(item.GetType().Name); - var typeFetcherOrder = typeOptions?.ImageFetcherOrder; + var fetcherOrder = typeOptions?.ImageFetcherOrder ?? options.ImageFetcherOrder; - return ImageProviders.Where(i => CanRefresh(i, item, libraryOptions, refreshOptions, includeDisabled)) - .OrderBy(i => + return _imageProviders.Where(i => CanRefreshImages(i, item, typeOptions, refreshOptions, includeDisabled)) + .OrderBy(i => GetConfiguredOrder(fetcherOrder, i.Name)) + .ThenBy(GetDefaultOrder); + } + + private bool CanRefreshImages( + IImageProvider provider, + BaseItem item, + TypeOptions? libraryTypeOptions, + ImageRefreshOptions refreshOptions, + bool includeDisabled) + { + try + { + if (!provider.Supports(item)) { - // See if there's a user-defined order - if (i is not ILocalImageProvider) - { - var fetcherOrder = typeFetcherOrder ?? currentOptions.ImageFetcherOrder; - var index = Array.IndexOf(fetcherOrder, i.Name); + return false; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "{ProviderName} failed in Supports for type {ItemType} at {ItemPath}", provider.GetType().Name, item.GetType().Name, item.Path); + return false; + } - if (index != -1) - { - return index; - } - } + if (includeDisabled || provider is ILocalImageProvider) + { + return true; + } - // Not configured. Just return some high number to put it at the end. - return 100; - }) - .ThenBy(GetOrder); + if (item.IsLocked && refreshOptions.ImageRefreshMode != MetadataRefreshMode.FullRefresh) + { + return false; + } + + return _baseItemManager.IsImageFetcherEnabled(item, libraryTypeOptions, provider.Name); } - /// <summary> - /// Gets the metadata providers for the provided item. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="libraryOptions">The library options.</param> - /// <typeparam name="T">The type of metadata provider.</typeparam> - /// <returns>The metadata providers.</returns> + /// <inheritdoc /> public IEnumerable<IMetadataProvider<T>> GetMetadataProviders<T>(BaseItem item, LibraryOptions libraryOptions) where T : BaseItem { @@ -367,165 +371,84 @@ namespace MediaBrowser.Providers.Manager private IEnumerable<IMetadataProvider<T>> GetMetadataProvidersInternal<T>(BaseItem item, LibraryOptions libraryOptions, MetadataOptions globalMetadataOptions, bool includeDisabled, bool forceEnableInternetMetadata) where T : BaseItem { - // Avoid implicitly captured closure - var currentOptions = globalMetadataOptions; + var localMetadataReaderOrder = libraryOptions.LocalMetadataReaderOrder ?? globalMetadataOptions.LocalMetadataReaderOrder; + var typeOptions = libraryOptions.GetTypeOptions(item.GetType().Name); + var metadataFetcherOrder = typeOptions?.MetadataFetcherOrder ?? globalMetadataOptions.MetadataFetcherOrder; return _metadataProviders.OfType<IMetadataProvider<T>>() - .Where(i => CanRefresh(i, item, libraryOptions, includeDisabled, forceEnableInternetMetadata)) - .OrderBy(i => GetConfiguredOrder(item, i, libraryOptions, globalMetadataOptions)) + .Where(i => CanRefreshMetadata(i, item, typeOptions, includeDisabled, forceEnableInternetMetadata)) + .OrderBy(i => + // local and remote providers will be interleaved in the final order + // only relative order within a type matters: consumers of the list filter to one or the other + i switch + { + ILocalMetadataProvider => GetConfiguredOrder(localMetadataReaderOrder, i.Name), + IRemoteMetadataProvider => GetConfiguredOrder(metadataFetcherOrder, i.Name), + // Default to end + _ => int.MaxValue + }) .ThenBy(GetDefaultOrder); } - private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(BaseItem item, bool includeDisabled) - { - var options = GetMetadataOptions(item); - var libraryOptions = _libraryManager.GetLibraryOptions(item); - - return GetImageProviders( - item, - libraryOptions, - options, - new ImageRefreshOptions(new DirectoryService(_fileSystem)), - includeDisabled).OfType<IRemoteImageProvider>(); - } - - private bool CanRefresh( + private bool CanRefreshMetadata( IMetadataProvider provider, BaseItem item, - LibraryOptions libraryOptions, + TypeOptions? libraryTypeOptions, bool includeDisabled, bool forceEnableInternetMetadata) { - if (!includeDisabled) - { - // If locked only allow local providers - if (item.IsLocked && provider is not ILocalMetadataProvider && provider is not IForcedProvider) - { - return false; - } - - if (provider is IRemoteMetadataProvider) - { - if (!forceEnableInternetMetadata && !_baseItemManager.IsMetadataFetcherEnabled(item, libraryOptions, provider.Name)) - { - return false; - } - } - } - if (!item.SupportsLocalMetadata && provider is ILocalMetadataProvider) { return false; } - // If this restriction is ever lifted, movie xml providers will have to be updated to prevent owned items like trailers from reading those files - if (!item.OwnerId.Equals(default)) + // Prevent owned items from reading the same local metadata file as their owner + if (!item.OwnerId.Equals(default) && provider is ILocalMetadataProvider) { - if (provider is ILocalMetadataProvider || provider is IRemoteMetadataProvider) - { - return false; - } + return false; } - return true; - } - - private bool CanRefresh( - IImageProvider provider, - BaseItem item, - LibraryOptions libraryOptions, - ImageRefreshOptions refreshOptions, - bool includeDisabled) - { - if (!includeDisabled) + if (includeDisabled) { - // If locked only allow local providers - if (item.IsLocked && provider is not ILocalImageProvider) - { - if (refreshOptions.ImageRefreshMode != MetadataRefreshMode.FullRefresh) - { - return false; - } - } - - if (provider is IRemoteImageProvider || provider is IDynamicImageProvider) - { - if (!_baseItemManager.IsImageFetcherEnabled(item, libraryOptions, provider.Name)) - { - return false; - } - } + return true; } - try - { - return provider.Supports(item); - } - catch (Exception ex) + // If locked only allow local providers + if (item.IsLocked && provider is not ILocalMetadataProvider && provider is not IForcedProvider) { - _logger.LogError(ex, "{ProviderName} failed in Supports for type {ItemType} at {ItemPath}", provider.GetType().Name, item.GetType().Name, item.Path); return false; } - } - /// <summary> - /// Gets the order. - /// </summary> - /// <param name="provider">The provider.</param> - /// <returns>System.Int32.</returns> - private int GetOrder(IImageProvider provider) - { - if (provider is not IHasOrder hasOrder) + if (forceEnableInternetMetadata || provider is not IRemoteMetadataProvider) { - return 0; + return true; } - return hasOrder.Order; + return _baseItemManager.IsMetadataFetcherEnabled(item, libraryTypeOptions, provider.Name); } - private int GetConfiguredOrder(BaseItem item, IMetadataProvider provider, LibraryOptions libraryOptions, MetadataOptions globalMetadataOptions) + private static int GetConfiguredOrder(string[] order, string providerName) { - // See if there's a user-defined order - if (provider is ILocalMetadataProvider) - { - var configuredOrder = libraryOptions.LocalMetadataReaderOrder ?? globalMetadataOptions.LocalMetadataReaderOrder; - - var index = Array.IndexOf(configuredOrder, provider.Name); - - if (index != -1) - { - return index; - } - } + var index = Array.IndexOf(order, providerName); - // See if there's a user-defined order - if (provider is IRemoteMetadataProvider) + if (index != -1) { - var typeOptions = libraryOptions.GetTypeOptions(item.GetType().Name); - var typeFetcherOrder = typeOptions?.MetadataFetcherOrder; - - var fetcherOrder = typeFetcherOrder ?? globalMetadataOptions.MetadataFetcherOrder; - - var index = Array.IndexOf(fetcherOrder, provider.Name); - - if (index != -1) - { - return index; - } + return index; } - // Not configured. Just return some high number to put it at the end. - return 100; + // default to end + return int.MaxValue; } - private int GetDefaultOrder(IMetadataProvider provider) + private static int GetDefaultOrder(object provider) { if (provider is IHasOrder hasOrder) { return hasOrder.Order; } - return 0; + // after items that want to be first (~0) but before items that want to be last (~100) + return 50; } /// <inheritdoc/> @@ -568,7 +491,7 @@ namespace MediaBrowser.Providers.Manager var libraryOptions = new LibraryOptions(); - var imageProviders = GetImageProviders( + var imageProviders = GetImageProvidersInternal( dummy, libraryOptions, options, @@ -677,7 +600,7 @@ namespace MediaBrowser.Providers.Manager foreach (var saver in savers.Where(i => IsSaverEnabledForItem(i, item, libraryOptions, updateType, false))) { - _logger.LogDebug("Saving {0} to {1}.", item.Path ?? item.Name, saver.Name); + _logger.LogDebug("Saving {Item} to {Saver}", item.Path ?? item.Name, saver.Name); if (saver is IMetadataFileSaver fileSaver) { @@ -689,7 +612,7 @@ namespace MediaBrowser.Providers.Manager } catch (Exception ex) { - _logger.LogError(ex, "Error in {0} GetSavePath", saver.Name); + _logger.LogError(ex, "Error in {Saver} GetSavePath", saver.Name); continue; } @@ -776,7 +699,7 @@ namespace MediaBrowser.Providers.Manager } catch (Exception ex) { - _logger.LogError(ex, "Error in {0}.IsEnabledFor", saver.Name); + _logger.LogError(ex, "Error in {Saver}.IsEnabledFor", saver.Name); return false; } } @@ -786,7 +709,7 @@ namespace MediaBrowser.Providers.Manager where TItemType : BaseItem, new() where TLookupType : ItemLookupInfo { - BaseItem referenceItem = null; + BaseItem? referenceItem = null; if (!searchInfo.ItemId.Equals(default)) { @@ -796,7 +719,7 @@ namespace MediaBrowser.Providers.Manager return GetRemoteSearchResults<TItemType, TLookupType>(searchInfo, referenceItem, cancellationToken); } - private async Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo, BaseItem referenceItem, CancellationToken cancellationToken) + private async Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo, BaseItem? referenceItem, CancellationToken cancellationToken) where TItemType : BaseItem, new() where TLookupType : ItemLookupInfo { @@ -926,7 +849,7 @@ namespace MediaBrowser.Providers.Manager } catch (Exception ex) { - _logger.LogError(ex, "Error in {0}.Supports", i.GetType().Name); + _logger.LogError(ex, "Error in {Type}.Supports", i.GetType().Name); return false; } }); @@ -958,7 +881,8 @@ namespace MediaBrowser.Providers.Manager i.UrlFormatString, value) }; - }).Where(i => i != null).Concat(item.GetRelatedUrls()); + }).Where(i => i != null) + .Concat(item.GetRelatedUrls())!; // We just filtered out all the nulls } /// <inheritdoc/> @@ -991,7 +915,7 @@ namespace MediaBrowser.Providers.Manager /// <inheritdoc/> public void OnRefreshStart(BaseItem item) { - _logger.LogDebug("OnRefreshStart {0}", item.Id.ToString("N", CultureInfo.InvariantCulture)); + _logger.LogDebug("OnRefreshStart {Item}", item.Id.ToString("N", CultureInfo.InvariantCulture)); _activeRefreshes[item.Id] = 0; RefreshStarted?.Invoke(this, new GenericEventArgs<BaseItem>(item)); } @@ -999,7 +923,7 @@ namespace MediaBrowser.Providers.Manager /// <inheritdoc/> public void OnRefreshComplete(BaseItem item) { - _logger.LogDebug("OnRefreshComplete {0}", item.Id.ToString("N", CultureInfo.InvariantCulture)); + _logger.LogDebug("OnRefreshComplete {Item}", item.Id.ToString("N", CultureInfo.InvariantCulture)); _activeRefreshes.Remove(item.Id, out _); @@ -1021,7 +945,7 @@ namespace MediaBrowser.Providers.Manager public void OnRefreshProgress(BaseItem item, double progress) { var id = item.Id; - _logger.LogDebug("OnRefreshProgress {0} {1}", id.ToString("N", CultureInfo.InvariantCulture), progress); + _logger.LogDebug("OnRefreshProgress {Id} {Progress}", id.ToString("N", CultureInfo.InvariantCulture), progress); // TODO: Need to hunt down the conditions for this happening _activeRefreshes.AddOrUpdate( |
