public async Task <ItemUpdateType> RefreshMetadata(IHasMetadata item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) { var itemOfType = (TItemType)item; var config = ProviderManager.GetMetadataOptions(item); var updateType = ItemUpdateType.None; var requiresRefresh = false; var libraryOptions = LibraryManager.GetLibraryOptions((BaseItem)item); if (!requiresRefresh && libraryOptions.AutomaticRefreshIntervalDays > 0 && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= libraryOptions.AutomaticRefreshIntervalDays) { requiresRefresh = true; } DateTime?newDateModified = null; if (item.LocationType == LocationType.FileSystem) { var file = refreshOptions.DirectoryService.GetFile(item.Path); if (file != null) { newDateModified = file.LastWriteTimeUtc; if (item.EnableRefreshOnDateModifiedChange) { if (newDateModified != item.DateModified) { Logger.Debug("Date modified for {0}. Old date {1} new date {2} Id {3}", item.Path, item.DateModified, newDateModified, item.Id); requiresRefresh = true; } } if (!requiresRefresh && item.SupportsLocalMetadata) { var video = item as Video; if (video != null && !video.IsPlaceHolder) { requiresRefresh = !video.SubtitleFiles .SequenceEqual(SubtitleResolver.GetSubtitleFiles(video, refreshOptions.DirectoryService, FileSystem, false) .OrderBy(i => i), StringComparer.OrdinalIgnoreCase); } } } } if (!requiresRefresh && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) { // TODO: If this returns true, should we instead just change metadata refresh mode to Full? requiresRefresh = item.RequiresRefresh(); } var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager, FileSystem); var localImagesFailed = false; var allImageProviders = ((ProviderManager)ProviderManager).GetImageProviders(item, refreshOptions).ToList(); // Start by validating images try { // Always validate images and check for new locally stored ones. if (itemImageProvider.ValidateImages(item, allImageProviders.OfType <ILocalImageProvider>(), refreshOptions.DirectoryService)) { updateType = updateType | ItemUpdateType.ImageUpdate; } } catch (Exception ex) { localImagesFailed = true; Logger.ErrorException("Error validating images for {0}", ex, item.Path ?? item.Name ?? "Unknown name"); } var metadataResult = new MetadataResult <TItemType> { Item = itemOfType }; bool hasRefreshedMetadata = true; bool hasRefreshedImages = true; var isFirstRefresh = item.DateLastRefreshed == default(DateTime); // Next run metadata providers if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) { var providers = GetProviders(item, refreshOptions, isFirstRefresh, requiresRefresh) .ToList(); if (providers.Count > 0 || isFirstRefresh) { if (item.BeforeMetadataRefresh()) { updateType = updateType | ItemUpdateType.MetadataImport; } } if (providers.Count > 0) { var id = itemOfType.GetLookupInfo(); if (refreshOptions.SearchResult != null) { ApplySearchResult(id, refreshOptions.SearchResult); } //await FindIdentities(id, cancellationToken).ConfigureAwait(false); id.IsAutomated = refreshOptions.IsAutomated; var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false); updateType = updateType | result.UpdateType; if (result.Failures > 0) { hasRefreshedMetadata = false; } } } // Next run remote image providers, but only if local image providers didn't throw an exception if (!localImagesFailed && refreshOptions.ImageRefreshMode != ImageRefreshMode.ValidationOnly) { var providers = GetNonLocalImageProviders(item, allImageProviders, refreshOptions).ToList(); if (providers.Count > 0) { var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, config, cancellationToken).ConfigureAwait(false); updateType = updateType | result.UpdateType; if (result.Failures > 0) { hasRefreshedImages = false; } } } var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh, updateType); updateType = updateType | beforeSaveResult; if (newDateModified.HasValue) { item.DateModified = newDateModified.Value; } // Save if changes were made, or it's never been saved before if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || isFirstRefresh || refreshOptions.ReplaceAllMetadata || requiresRefresh) { // If any of these properties are set then make sure the updateType is not None, just to force everything to save if (refreshOptions.ForceSave || refreshOptions.ReplaceAllMetadata) { updateType = updateType | ItemUpdateType.MetadataDownload; } if (hasRefreshedMetadata && hasRefreshedImages) { item.DateLastRefreshed = DateTime.UtcNow; } else { item.DateLastRefreshed = default(DateTime); } // Save to database await SaveItem(metadataResult, libraryOptions, updateType, cancellationToken).ConfigureAwait(false); } await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false); return(updateType); }