diff options
| author | Shadowghost <Ghost_of_Stone@web.de> | 2026-08-22 08:50:14 +0200 |
|---|---|---|
| committer | Shadowghost <Ghost_of_Stone@web.de> | 2026-08-22 08:50:14 +0200 |
| commit | 42c70fba63d72a4113a7b8f2e06293e512cc6914 (patch) | |
| tree | a0a8ef9fac67796919460208ce231a55b0ea0dd2 /MediaBrowser.Providers/Manager/MetadataService.cs | |
| parent | bd085665d687b55298fde7b9ef6d7124a5f94701 (diff) | |
Additional fixes
Co-Authored-By: Cody Robibero <cody@robibe.ro>
Diffstat (limited to 'MediaBrowser.Providers/Manager/MetadataService.cs')
| -rw-r--r-- | MediaBrowser.Providers/Manager/MetadataService.cs | 107 |
1 files changed, 87 insertions, 20 deletions
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index c6c15198be..d11db8f531 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -260,21 +260,40 @@ namespace MediaBrowser.Providers.Manager switch (lookupInfo) { case EpisodeInfo episodeInfo: - episodeInfo.SeriesProviderIds = result.ProviderIds; + episodeInfo.SeriesProviderIds = GetValidProviderIds(result.ProviderIds); episodeInfo.ProviderIds.Clear(); break; case SeasonInfo seasonInfo: - seasonInfo.SeriesProviderIds = result.ProviderIds; + seasonInfo.SeriesProviderIds = GetValidProviderIds(result.ProviderIds); seasonInfo.ProviderIds.Clear(); break; default: - lookupInfo.ProviderIds = result.ProviderIds; + lookupInfo.SetProviderIds(result.ProviderIds); lookupInfo.Name = result.Name; lookupInfo.Year = result.ProductionYear; break; } } + private static Dictionary<string, string> GetValidProviderIds(IReadOnlyDictionary<string, string> providerIds) + { + var validProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + if (providerIds is null) + { + return validProviderIds; + } + + foreach (var (name, value) in providerIds) + { + if (ProviderIdsExtensions.IsValidProviderId(name, value)) + { + validProviderIds[name] = value; + } + } + + return validProviderIds; + } + protected async Task SaveItemAsync(MetadataResult<TItemType> result, ItemUpdateType reason, bool reattachUserData, CancellationToken cancellationToken) { await result.Item.UpdateToRepositoryAsync(reason, cancellationToken).ConfigureAwait(false); @@ -835,6 +854,7 @@ namespace MediaBrowser.Providers.Manager } } + var hasRemoteMetadata = false; var isLocalLocked = temp.Item.IsLocked; if (!isLocalLocked && (options.ReplaceAllMetadata || options.MetadataRefreshMode > MetadataRefreshMode.ValidationOnly)) { @@ -849,6 +869,7 @@ namespace MediaBrowser.Providers.Manager var remoteResult = await ExecuteRemoteProviders(temp, logName, false, id, remoteProviders, cancellationToken).ConfigureAwait(false); + hasRemoteMetadata = remoteResult.UpdateType.HasFlag(ItemUpdateType.MetadataDownload); refreshResult.UpdateType |= remoteResult.UpdateType; refreshResult.ErrorMessage = remoteResult.ErrorMessage; refreshResult.Failures += remoteResult.Failures; @@ -858,10 +879,12 @@ namespace MediaBrowser.Providers.Manager { if (refreshResult.UpdateType > ItemUpdateType.None) { - // A provider that failed contributed nothing, so the result is not the complete - // replacement the caller asked for. Keeping the existing values stops a provider being - // temporarily unreachable, or choking on a bad id, from deleting the data it owns. - if (!options.RemoveOldMetadata || refreshResult.Failures > 0) + // Erasing the old values is only safe when a remote provider returned something to + // replace them with. If every one of them failed there is no replacement, and wiping the + // item would turn a provider being temporarily unreachable into permanent data loss. + // A single failure is not enough: Identify asks for the erasure precisely because the + // previous match was wrong, and an unrelated provider throwing must not undo that. + if (!options.RemoveOldMetadata || (refreshResult.Failures > 0 && !hasRemoteMetadata)) { // Add existing metadata to provider result if it does not exist there MergeData(metadata, temp, [], false, false); @@ -935,7 +958,7 @@ namespace MediaBrowser.Providers.Manager { result.Provider = provider.Name; - LogInvalidProviderIds(result.Item, providerName, logName); + LogInvalidProviderIds(result, providerName, logName); MergeData(result, temp, [], replaceData, false); MergeNewData(temp.Item, id); @@ -969,19 +992,48 @@ namespace MediaBrowser.Providers.Manager /// The ids are dropped when merging, this names the provider that produced them so the source of a /// recurring bad id can be found. /// </remarks> - private void LogInvalidProviderIds(TItemType item, string providerName, string logName) + private void LogInvalidProviderIds(MetadataResult<TItemType> result, string providerName, string logName) + { + if (!Logger.IsEnabled(LogLevel.Debug)) + { + return; + } + + LogInvalidProviderIds(result.Item?.ProviderIds, providerName, logName, null); + + if (result.People is null) + { + return; + } + + foreach (var person in result.People) + { + LogInvalidProviderIds(person.ProviderIds, providerName, logName, person.Name); + } + } + + private void LogInvalidProviderIds(IReadOnlyDictionary<string, string> providerIds, string providerName, string logName, string personName) { - if (item?.ProviderIds is null || !Logger.IsEnabled(LogLevel.Debug)) + if (providerIds is null) { return; } - foreach (var (key, value) in item.ProviderIds) + foreach (var (key, value) in providerIds) { - if (!ProviderIdsExtensions.IsValidProviderId(key, value)) + if (ProviderIdsExtensions.IsValidProviderId(key, value)) + { + continue; + } + + if (personName is null) { Logger.LogDebug("Discarding {Key} id '{Value}' returned by {Provider} for {Item}", key, value, providerName, logName); } + else + { + Logger.LogDebug("Discarding {Key} id '{Value}' returned by {Provider} for {Person} of {Item}", key, value, providerName, personName, logName); + } } } @@ -997,8 +1049,13 @@ namespace MediaBrowser.Providers.Manager continue; } - // Don't replace existing Id's. - lookupInfo.ProviderIds.TryAdd(key, providerId.Value); + // Don't replace existing Id's, unless the one already there is unusable - handing that + // one to the providers that have yet to run is what makes them fail. + if (!lookupInfo.ProviderIds.TryGetValue(key, out var existingId) + || !ProviderIdsExtensions.IsValidProviderId(key, existingId)) + { + lookupInfo.ProviderIds[key] = providerId.Value; + } } } @@ -1138,6 +1195,7 @@ namespace MediaBrowser.Providers.Manager if (!lockedFields.Contains(MetadataField.Cast)) { RemoveInvalidProviderIds(sourceResult.People); + RemoveInvalidProviderIds(targetResult.People); if (replaceData || targetResult.People is null || targetResult.People.Count == 0) { @@ -1217,15 +1275,24 @@ namespace MediaBrowser.Providers.Manager continue; } - // Don't replace existing Id's. - if (replaceData) + // Don't replace existing Id's, unless the stored one is unusable - that one is the bad + // match the refresh is meant to repair. + if (replaceData + || !target.ProviderIds.TryGetValue(key, out var existingId) + || !ProviderIdsExtensions.IsValidProviderId(key, existingId)) { target.ProviderIds[key] = id.Value; } - else - { - target.ProviderIds.TryAdd(key, id.Value); - } + } + + // A bad id no provider offered a replacement for still has to go, otherwise the item keeps + // failing the same way on every refresh. + foreach (var key in target.ProviderIds + .Where(id => !ProviderIdsExtensions.IsValidProviderId(id.Key, id.Value)) + .Select(id => id.Key) + .ToArray()) + { + target.ProviderIds.Remove(key); } if (replaceData || !target.CriticRating.HasValue) |
