/// <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);
        }