/// <summary> /// Runs all metadata providers for an entity, and returns true or false indicating if at least one was refreshed and requires persistence /// </summary> /// <param name="item">The item.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="force">if set to <c>true</c> [force].</param> /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param> /// <returns>Task{System.Boolean}.</returns> public async Task <bool> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true) { // Allow providers of the same priority to execute in parallel MetadataProviderPriority?currentPriority = null; var currentTasks = new List <Task <bool> >(); var result = false; cancellationToken.ThrowIfCancellationRequested(); // Determine if supported providers have changed var supportedProviders = MetadataProviders.Where(p => p.Supports(item)).ToList(); BaseProviderInfo supportedProvidersInfo; if (SupportedProvidersKey == Guid.Empty) { SupportedProvidersKey = "SupportedProviders".GetMD5(); } var supportedProvidersHash = string.Join("+", supportedProviders.Select(i => i.GetType().Name)).GetMD5(); bool providersChanged; item.ProviderData.TryGetValue(SupportedProvidersKey, out supportedProvidersInfo); if (supportedProvidersInfo == null) { // First time supportedProvidersInfo = new BaseProviderInfo { ProviderId = SupportedProvidersKey, FileSystemStamp = supportedProvidersHash }; providersChanged = force = true; } else { // Force refresh if the supported providers have changed providersChanged = force = force || supportedProvidersInfo.FileSystemStamp != supportedProvidersHash; } // If providers have changed, clear provider info and update the supported providers hash if (providersChanged) { _logger.Debug("Providers changed for {0}. Clearing and forcing refresh.", item.Name); item.ProviderData.Clear(); supportedProvidersInfo.FileSystemStamp = supportedProvidersHash; } if (force) { item.ClearMetaValues(); } // Run the normal providers sequentially in order of priority foreach (var provider in supportedProviders) { cancellationToken.ThrowIfCancellationRequested(); // Skip if internet providers are currently disabled if (provider.RequiresInternet && !ConfigurationManager.Configuration.EnableInternetProviders) { continue; } // Skip if is slow and we aren't allowing slow ones if (provider.IsSlow && !allowSlowProviders) { continue; } // Skip if internet provider and this type is not allowed if (provider.RequiresInternet && ConfigurationManager.Configuration.EnableInternetProviders && ConfigurationManager.Configuration.InternetProviderExcludeTypes.Contains(item.GetType().Name, StringComparer.OrdinalIgnoreCase)) { continue; } // When a new priority is reached, await the ones that are currently running and clear the list if (currentPriority.HasValue && currentPriority.Value != provider.Priority && currentTasks.Count > 0) { var results = await Task.WhenAll(currentTasks).ConfigureAwait(false); result |= results.Contains(true); currentTasks.Clear(); } // Put this check below the await because the needs refresh of the next tier of providers may depend on the previous ones running // This is the case for the fan art provider which depends on the movie and tv providers having run before them if (!force && !provider.NeedsRefresh(item)) { continue; } currentTasks.Add(FetchAsync(provider, item, force, cancellationToken)); currentPriority = provider.Priority; } if (currentTasks.Count > 0) { var results = await Task.WhenAll(currentTasks).ConfigureAwait(false); result |= results.Contains(true); } if (providersChanged) { item.ProviderData[SupportedProvidersKey] = supportedProvidersInfo; } return(result || providersChanged); }