소스 검색

Merge pull request #11743 from Shadowghost/fix-replace

Fix replace logic
Joshua M. Boniface 1 년 전
부모
커밋
2ddb15c784

+ 5 - 4
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -5221,19 +5221,20 @@ AND Type = @InternalPersonType)");
                 throw new ArgumentNullException(nameof(itemId));
             }
 
-            ArgumentNullException.ThrowIfNull(people);
-
             CheckDisposed();
 
             using var connection = GetConnection();
             using var transaction = connection.BeginTransaction();
-            // First delete chapters
+            // Delete all existing people first
             using var command = connection.CreateCommand();
             command.CommandText = "delete from People where ItemId=@ItemId";
             command.TryBind("@ItemId", itemId);
             command.ExecuteNonQuery();
 
-            InsertPeople(itemId, people, connection);
+            if (people is not null)
+            {
+                InsertPeople(itemId, people, connection);
+            }
 
             transaction.Commit();
         }

+ 4 - 2
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -2812,8 +2812,10 @@ namespace Emby.Server.Implementations.Library
             }
 
             _itemRepository.UpdatePeople(item.Id, people);
-
-            await SavePeopleMetadataAsync(people, cancellationToken).ConfigureAwait(false);
+            if (people is not null)
+            {
+                await SavePeopleMetadataAsync(people, cancellationToken).ConfigureAwait(false);
+            }
         }
 
         public async Task<ItemImageInfo> ConvertImageToLocal(BaseItem item, ItemImageInfo image, int imageIndex, bool removeOnFailure)

+ 44 - 8
Jellyfin.Api/Controllers/ItemUpdateController.cs

@@ -290,17 +290,35 @@ public class ItemUpdateController : BaseJellyfinApiController
         {
             foreach (var season in rseries.Children.OfType<Season>())
             {
-                season.OfficialRating = request.OfficialRating;
+                if (!season.LockedFields.Contains(MetadataField.OfficialRating))
+                {
+                    season.OfficialRating = request.OfficialRating;
+                }
+
                 season.CustomRating = request.CustomRating;
-                season.Tags = season.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray();
+
+                if (!season.LockedFields.Contains(MetadataField.Tags))
+                {
+                    season.Tags = season.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray();
+                }
+
                 season.OnMetadataChanged();
                 await season.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
 
                 foreach (var ep in season.Children.OfType<Episode>())
                 {
-                    ep.OfficialRating = request.OfficialRating;
+                    if (!ep.LockedFields.Contains(MetadataField.OfficialRating))
+                    {
+                        ep.OfficialRating = request.OfficialRating;
+                    }
+
                     ep.CustomRating = request.CustomRating;
-                    ep.Tags = ep.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray();
+
+                    if (!ep.LockedFields.Contains(MetadataField.Tags))
+                    {
+                        ep.Tags = ep.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray();
+                    }
+
                     ep.OnMetadataChanged();
                     await ep.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
                 }
@@ -310,9 +328,18 @@ public class ItemUpdateController : BaseJellyfinApiController
         {
             foreach (var ep in season.Children.OfType<Episode>())
             {
-                ep.OfficialRating = request.OfficialRating;
+                if (!ep.LockedFields.Contains(MetadataField.OfficialRating))
+                {
+                    ep.OfficialRating = request.OfficialRating;
+                }
+
                 ep.CustomRating = request.CustomRating;
-                ep.Tags = ep.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray();
+
+                if (!ep.LockedFields.Contains(MetadataField.Tags))
+                {
+                    ep.Tags = ep.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray();
+                }
+
                 ep.OnMetadataChanged();
                 await ep.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
             }
@@ -321,9 +348,18 @@ public class ItemUpdateController : BaseJellyfinApiController
         {
             foreach (BaseItem track in album.Children)
             {
-                track.OfficialRating = request.OfficialRating;
+                if (!track.LockedFields.Contains(MetadataField.OfficialRating))
+                {
+                    track.OfficialRating = request.OfficialRating;
+                }
+
                 track.CustomRating = request.CustomRating;
-                track.Tags = track.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray();
+
+                if (!track.LockedFields.Contains(MetadataField.Tags))
+                {
+                    track.Tags = track.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray();
+                }
+
                 track.OnMetadataChanged();
                 await track.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
             }

+ 0 - 3
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -751,9 +751,6 @@ namespace MediaBrowser.Controller.Entities
         [JsonIgnore]
         public virtual bool SupportsAncestors => true;
 
-        [JsonIgnore]
-        public virtual bool StopRefreshIfLocalMetadataFound => true;
-
         [JsonIgnore]
         protected virtual bool SupportsOwnedItems => !ParentId.IsEmpty() && IsFileProtocol;
 

+ 0 - 3
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -45,9 +45,6 @@ namespace MediaBrowser.Controller.Entities.Movies
             set => TmdbCollectionName = value;
         }
 
-        [JsonIgnore]
-        public override bool StopRefreshIfLocalMetadataFound => false;
-
         public override double GetDefaultPrimaryImageAspectRatio()
         {
             // hack for tv plugins

+ 1 - 5
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -69,9 +69,6 @@ namespace MediaBrowser.Controller.Entities.TV
         /// <value>The status.</value>
         public SeriesStatus? Status { get; set; }
 
-        [JsonIgnore]
-        public override bool StopRefreshIfLocalMetadataFound => false;
-
         public override double GetDefaultPrimaryImageAspectRatio()
         {
             double value = 2;
@@ -288,8 +285,7 @@ namespace MediaBrowser.Controller.Entities.TV
 
         public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
         {
-            // Refresh bottom up, children first, then the boxset
-            // By then hopefully the  movies within will have Tmdb collection values
+            // Refresh bottom up, seasons and episodes first, then the series
             var items = GetRecursiveChildren();
 
             var totalItems = items.Count;

+ 0 - 3
MediaBrowser.Controller/Entities/Trailer.cs

@@ -23,9 +23,6 @@ namespace MediaBrowser.Controller.Entities
             TrailerTypes = Array.Empty<TrailerType>();
         }
 
-        [JsonIgnore]
-        public override bool StopRefreshIfLocalMetadataFound => false;
-
         public TrailerType[] TrailerTypes { get; set; }
 
         public override double GetDefaultPrimaryImageAspectRatio()

+ 86 - 79
MediaBrowser.Providers/Manager/MetadataService.cs

@@ -121,7 +121,8 @@ namespace MediaBrowser.Providers.Manager
 
             var metadataResult = new MetadataResult<TItemType>
             {
-                Item = itemOfType
+                Item = itemOfType,
+                People = LibraryManager.GetPeople(item)
             };
 
             bool hasRefreshedMetadata = true;
@@ -164,7 +165,7 @@ namespace MediaBrowser.Providers.Manager
             }
 
             // Next run remote image providers, but only if local image providers didn't throw an exception
-            if (!localImagesFailed && refreshOptions.ImageRefreshMode != MetadataRefreshMode.ValidationOnly)
+            if (!localImagesFailed && refreshOptions.ImageRefreshMode > MetadataRefreshMode.ValidationOnly)
             {
                 var providers = GetNonLocalImageProviders(item, allImageProviders, refreshOptions).ToList();
 
@@ -242,7 +243,7 @@ namespace MediaBrowser.Providers.Manager
 
         protected async Task SaveItemAsync(MetadataResult<TItemType> result, ItemUpdateType reason, CancellationToken cancellationToken)
         {
-            if (result.Item.SupportsPeople && result.People is not null)
+            if (result.Item.SupportsPeople)
             {
                 var baseItem = result.Item;
 
@@ -655,26 +656,19 @@ namespace MediaBrowser.Providers.Manager
                 await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false);
             }
 
+            if (item.IsLocked)
+            {
+                return refreshResult;
+            }
+
             var temp = new MetadataResult<TItemType>
             {
                 Item = CreateNew()
             };
             temp.Item.Path = item.Path;
+            temp.Item.Id = item.Id;
 
-            // If replacing all metadata, run internet providers first
-            if (options.ReplaceAllMetadata)
-            {
-                var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType<IRemoteMetadataProvider<TItemType, TIdType>>(), cancellationToken)
-                    .ConfigureAwait(false);
-
-                refreshResult.UpdateType |= remoteResult.UpdateType;
-                refreshResult.ErrorMessage = remoteResult.ErrorMessage;
-                refreshResult.Failures += remoteResult.Failures;
-            }
-
-            var hasLocalMetadata = false;
             var foundImageTypes = new List<ImageType>();
-
             foreach (var provider in providers.OfType<ILocalMetadataProvider<TItemType>>())
             {
                 var providerName = provider.GetType().Name;
@@ -720,15 +714,9 @@ namespace MediaBrowser.Providers.Manager
                             refreshResult.UpdateType |= ItemUpdateType.ImageUpdate;
                         }
 
-                        MergeData(localItem, temp, Array.Empty<MetadataField>(), options.ReplaceAllMetadata, true);
+                        MergeData(localItem, temp, Array.Empty<MetadataField>(), false, true);
                         refreshResult.UpdateType |= ItemUpdateType.MetadataImport;
 
-                        // Only one local provider allowed per item
-                        if (item.IsLocked || localItem.Item.IsLocked || IsFullLocalMetadata(localItem.Item))
-                        {
-                            hasLocalMetadata = true;
-                        }
-
                         break;
                     }
 
@@ -747,10 +735,10 @@ namespace MediaBrowser.Providers.Manager
                 }
             }
 
-            // Local metadata is king - if any is found don't run remote providers
-            if (!options.ReplaceAllMetadata && (!hasLocalMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || !item.StopRefreshIfLocalMetadataFound))
+            var isLocalLocked = temp.Item.IsLocked;
+            if (!isLocalLocked && (options.ReplaceAllMetadata || options.MetadataRefreshMode > MetadataRefreshMode.ValidationOnly))
             {
-                var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType<IRemoteMetadataProvider<TItemType, TIdType>>(), cancellationToken)
+                var remoteResult = await ExecuteRemoteProviders(temp, logName, false, id, providers.OfType<IRemoteMetadataProvider<TItemType, TIdType>>(), cancellationToken)
                     .ConfigureAwait(false);
 
                 refreshResult.UpdateType |= remoteResult.UpdateType;
@@ -762,19 +750,20 @@ namespace MediaBrowser.Providers.Manager
             {
                 if (refreshResult.UpdateType > ItemUpdateType.None)
                 {
-                    if (hasLocalMetadata)
+                    if (!options.RemoveOldMetadata)
+                    {
+                        // Add existing metadata to provider result if it does not exist there
+                        MergeData(metadata, temp, Array.Empty<MetadataField>(), false, false);
+                    }
+
+                    if (isLocalLocked)
                     {
                         MergeData(temp, metadata, item.LockedFields, true, true);
                     }
                     else
                     {
-                        if (!options.RemoveOldMetadata)
-                        {
-                            MergeData(metadata, temp, Array.Empty<MetadataField>(), false, false);
-                        }
-
-                        // Will always replace all metadata when Scan for new and updated files is used. Else, follow the options.
-                        MergeData(temp, metadata, item.LockedFields, options.MetadataRefreshMode == MetadataRefreshMode.Default || options.ReplaceAllMetadata, false);
+                        var shouldReplace = options.MetadataRefreshMode > MetadataRefreshMode.ValidationOnly || options.ReplaceAllMetadata;
+                        MergeData(temp, metadata, item.LockedFields, shouldReplace, false);
                     }
                 }
             }
@@ -787,16 +776,6 @@ namespace MediaBrowser.Providers.Manager
             return refreshResult;
         }
 
-        protected virtual bool IsFullLocalMetadata(TItemType item)
-        {
-            if (string.IsNullOrWhiteSpace(item.Name))
-            {
-                return false;
-            }
-
-            return true;
-        }
-
         private async Task RunCustomProvider(ICustomMetadataProvider<TItemType> provider, TItemType item, string logName, MetadataRefreshOptions options, RefreshResult refreshResult, CancellationToken cancellationToken)
         {
             Logger.LogDebug("Running {Provider} for {Item}", provider.GetType().Name, logName);
@@ -821,7 +800,7 @@ namespace MediaBrowser.Providers.Manager
             return new TItemType();
         }
 
-        private async Task<RefreshResult> ExecuteRemoteProviders(MetadataResult<TItemType> temp, string logName, TIdType id, IEnumerable<IRemoteMetadataProvider<TItemType, TIdType>> providers, CancellationToken cancellationToken)
+        private async Task<RefreshResult> ExecuteRemoteProviders(MetadataResult<TItemType> temp, string logName, bool replaceData, TIdType id, IEnumerable<IRemoteMetadataProvider<TItemType, TIdType>> providers, CancellationToken cancellationToken)
         {
             var refreshResult = new RefreshResult();
 
@@ -846,7 +825,7 @@ namespace MediaBrowser.Providers.Manager
                     {
                         result.Provider = provider.Name;
 
-                        MergeData(result, temp, Array.Empty<MetadataField>(), false, false);
+                        MergeData(result, temp, Array.Empty<MetadataField>(), replaceData, false);
                         MergeNewData(temp.Item, id);
 
                         refreshResult.UpdateType |= ItemUpdateType.MetadataDownload;
@@ -949,11 +928,7 @@ namespace MediaBrowser.Providers.Manager
 
             if (replaceData || string.IsNullOrEmpty(target.OriginalTitle))
             {
-                // Safeguard against incoming data having an empty name
-                if (!string.IsNullOrWhiteSpace(source.OriginalTitle))
-                {
-                    target.OriginalTitle = source.OriginalTitle;
-                }
+                target.OriginalTitle = source.OriginalTitle;
             }
 
             if (replaceData || !target.CommunityRating.HasValue)
@@ -1016,7 +991,7 @@ namespace MediaBrowser.Providers.Manager
                 {
                     targetResult.People = sourceResult.People;
                 }
-                else if (targetResult.People is not null && sourceResult.People is not null)
+                else if (sourceResult.People is not null && sourceResult.People.Count >= 0)
                 {
                     MergePeople(sourceResult.People, targetResult.People);
                 }
@@ -1049,6 +1024,10 @@ namespace MediaBrowser.Providers.Manager
                 {
                     target.Studios = source.Studios;
                 }
+                else
+                {
+                    target.Studios = target.Studios.Concat(source.Studios).Distinct().ToArray();
+                }
             }
 
             if (!lockedFields.Contains(MetadataField.Tags))
@@ -1057,6 +1036,10 @@ namespace MediaBrowser.Providers.Manager
                 {
                     target.Tags = source.Tags;
                 }
+                else
+                {
+                    target.Tags = target.Tags.Concat(source.Tags).Distinct().ToArray();
+                }
             }
 
             if (!lockedFields.Contains(MetadataField.ProductionLocations))
@@ -1065,6 +1048,10 @@ namespace MediaBrowser.Providers.Manager
                 {
                     target.ProductionLocations = source.ProductionLocations;
                 }
+                else
+                {
+                    target.Tags = target.ProductionLocations.Concat(source.ProductionLocations).Distinct().ToArray();
+                }
             }
 
             foreach (var id in source.ProviderIds)
@@ -1082,17 +1069,28 @@ namespace MediaBrowser.Providers.Manager
                 }
             }
 
+            if (replaceData || !target.CriticRating.HasValue)
+            {
+                target.CriticRating = source.CriticRating;
+            }
+
+            if (replaceData || target.RemoteTrailers.Count == 0)
+            {
+                target.RemoteTrailers = source.RemoteTrailers;
+            }
+            else
+            {
+                target.RemoteTrailers = target.RemoteTrailers.Concat(source.RemoteTrailers).Distinct().ToArray();
+            }
+
             MergeAlbumArtist(source, target, replaceData);
-            MergeCriticRating(source, target, replaceData);
-            MergeTrailers(source, target, replaceData);
             MergeVideoInfo(source, target, replaceData);
             MergeDisplayOrder(source, target, replaceData);
 
             if (replaceData || string.IsNullOrEmpty(target.ForcedSortName))
             {
                 var forcedSortName = source.ForcedSortName;
-
-                if (!string.IsNullOrWhiteSpace(forcedSortName))
+                if (!string.IsNullOrEmpty(forcedSortName))
                 {
                     target.ForcedSortName = forcedSortName;
                 }
@@ -1100,22 +1098,44 @@ namespace MediaBrowser.Providers.Manager
 
             if (mergeMetadataSettings)
             {
-                target.LockedFields = source.LockedFields;
-                target.IsLocked = source.IsLocked;
+                if (replaceData || !target.IsLocked)
+                {
+                    target.IsLocked = target.IsLocked || source.IsLocked;
+                }
+
+                if (target.LockedFields.Length == 0)
+                {
+                    target.LockedFields = source.LockedFields;
+                }
+                else
+                {
+                    target.LockedFields = target.LockedFields.Concat(source.LockedFields).Distinct().ToArray();
+                }
 
-                // Grab the value if it's there, but if not then don't overwrite with the default
                 if (source.DateCreated != default)
                 {
                     target.DateCreated = source.DateCreated;
                 }
 
-                target.PreferredMetadataCountryCode = source.PreferredMetadataCountryCode;
-                target.PreferredMetadataLanguage = source.PreferredMetadataLanguage;
+                if (replaceData || string.IsNullOrEmpty(target.PreferredMetadataCountryCode))
+                {
+                    target.PreferredMetadataCountryCode = source.PreferredMetadataCountryCode;
+                }
+
+                if (replaceData || string.IsNullOrEmpty(target.PreferredMetadataLanguage))
+                {
+                    target.PreferredMetadataLanguage = source.PreferredMetadataLanguage;
+                }
             }
         }
 
         private static void MergePeople(List<PersonInfo> source, List<PersonInfo> target)
         {
+            if (target is null)
+            {
+                target = new List<PersonInfo>();
+            }
+
             foreach (var person in target)
             {
                 var normalizedName = person.Name.RemoveDiacritics();
@@ -1144,7 +1164,6 @@ namespace MediaBrowser.Providers.Manager
                 if (replaceData || string.IsNullOrEmpty(targetHasDisplayOrder.DisplayOrder))
                 {
                     var displayOrder = sourceHasDisplayOrder.DisplayOrder;
-
                     if (!string.IsNullOrWhiteSpace(displayOrder))
                     {
                         targetHasDisplayOrder.DisplayOrder = displayOrder;
@@ -1162,22 +1181,10 @@ namespace MediaBrowser.Providers.Manager
                 {
                     targetHasAlbumArtist.AlbumArtists = sourceHasAlbumArtist.AlbumArtists;
                 }
-            }
-        }
-
-        private static void MergeCriticRating(BaseItem source, BaseItem target, bool replaceData)
-        {
-            if (replaceData || !target.CriticRating.HasValue)
-            {
-                target.CriticRating = source.CriticRating;
-            }
-        }
-
-        private static void MergeTrailers(BaseItem source, BaseItem target, bool replaceData)
-        {
-            if (replaceData || target.RemoteTrailers.Count == 0)
-            {
-                target.RemoteTrailers = source.RemoteTrailers;
+                else if (sourceHasAlbumArtist.AlbumArtists.Count >= 0)
+                {
+                    targetHasAlbumArtist.AlbumArtists = targetHasAlbumArtist.AlbumArtists.Concat(sourceHasAlbumArtist.AlbumArtists).Distinct().ToArray();
+                }
             }
         }
 
@@ -1185,7 +1192,7 @@ namespace MediaBrowser.Providers.Manager
         {
             if (source is Video sourceCast && target is Video targetCast)
             {
-                if (replaceData || targetCast.Video3DFormat is null)
+                if (replaceData || !targetCast.Video3DFormat.HasValue)
                 {
                     targetCast.Video3DFormat = sourceCast.Video3DFormat;
                 }

+ 0 - 16
MediaBrowser.Providers/Movies/MovieMetadataService.cs

@@ -23,22 +23,6 @@ namespace MediaBrowser.Providers.Movies
         {
         }
 
-        /// <inheritdoc />
-        protected override bool IsFullLocalMetadata(Movie item)
-        {
-            if (string.IsNullOrWhiteSpace(item.Overview))
-            {
-                return false;
-            }
-
-            if (!item.ProductionYear.HasValue)
-            {
-                return false;
-            }
-
-            return base.IsFullLocalMetadata(item);
-        }
-
         /// <inheritdoc />
         protected override void MergeData(MetadataResult<Movie> source, MetadataResult<Movie> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
         {

+ 5 - 16
MediaBrowser.Providers/Movies/TrailerMetadataService.cs

@@ -1,5 +1,6 @@
 #pragma warning disable CS1591
 
+using System.Linq;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
@@ -23,22 +24,6 @@ namespace MediaBrowser.Providers.Movies
         {
         }
 
-        /// <inheritdoc />
-        protected override bool IsFullLocalMetadata(Trailer item)
-        {
-            if (string.IsNullOrWhiteSpace(item.Overview))
-            {
-                return false;
-            }
-
-            if (!item.ProductionYear.HasValue)
-            {
-                return false;
-            }
-
-            return base.IsFullLocalMetadata(item);
-        }
-
         /// <inheritdoc />
         protected override void MergeData(MetadataResult<Trailer> source, MetadataResult<Trailer> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
@@ -48,6 +33,10 @@ namespace MediaBrowser.Providers.Movies
             {
                 target.Item.TrailerTypes = source.Item.TrailerTypes;
             }
+            else
+            {
+                target.Item.TrailerTypes = target.Item.TrailerTypes.Concat(source.Item.TrailerTypes).Distinct().ToArray();
+            }
         }
     }
 }

+ 4 - 0
MediaBrowser.Providers/Music/AlbumMetadataService.cs

@@ -225,6 +225,10 @@ namespace MediaBrowser.Providers.Music
             {
                 targetItem.Artists = sourceItem.Artists;
             }
+            else
+            {
+                targetItem.Artists = targetItem.Artists.Concat(sourceItem.Artists).Distinct().ToArray();
+            }
 
             if (replaceData || string.IsNullOrEmpty(targetItem.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist)))
             {

+ 5 - 0
MediaBrowser.Providers/Music/AudioMetadataService.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Linq;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Library;
@@ -60,6 +61,10 @@ namespace MediaBrowser.Providers.Music
             {
                 targetItem.Artists = sourceItem.Artists;
             }
+            else
+            {
+                targetItem.Artists = targetItem.Artists.Concat(sourceItem.Artists).Distinct().ToArray();
+            }
 
             if (replaceData || string.IsNullOrEmpty(targetItem.Album))
             {

+ 5 - 0
MediaBrowser.Providers/Music/MusicVideoMetadataService.cs

@@ -1,5 +1,6 @@
 #pragma warning disable CS1591
 
+using System.Linq;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
@@ -45,6 +46,10 @@ namespace MediaBrowser.Providers.Music
             {
                 targetItem.Artists = sourceItem.Artists;
             }
+            else
+            {
+                targetItem.Artists = targetItem.Artists.Concat(sourceItem.Artists).Distinct().ToArray();
+            }
         }
     }
 }

+ 19 - 2
MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs

@@ -1,6 +1,7 @@
 #pragma warning disable CS1591
 
 using System.Collections.Generic;
+using System.Linq;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
@@ -49,8 +50,24 @@ namespace MediaBrowser.Providers.Playlists
             if (mergeMetadataSettings)
             {
                 targetItem.PlaylistMediaType = sourceItem.PlaylistMediaType;
-                targetItem.LinkedChildren = sourceItem.LinkedChildren;
-                targetItem.Shares = sourceItem.Shares;
+
+                if (replaceData || targetItem.LinkedChildren.Length == 0)
+                {
+                    targetItem.LinkedChildren = sourceItem.LinkedChildren;
+                }
+                else
+                {
+                    targetItem.LinkedChildren = sourceItem.LinkedChildren.Concat(targetItem.LinkedChildren).Distinct().ToArray();
+                }
+
+                if (replaceData || targetItem.Shares.Count == 0)
+                {
+                    targetItem.Shares = sourceItem.Shares;
+                }
+                else
+                {
+                    targetItem.Shares = sourceItem.Shares.Concat(targetItem.Shares).DistinctBy(s => s.UserId).ToArray();
+                }
             }
         }
     }

+ 0 - 16
MediaBrowser.Providers/TV/SeriesMetadataService.cs

@@ -65,22 +65,6 @@ namespace MediaBrowser.Providers.TV
             await CreateSeasonsAsync(item, cancellationToken).ConfigureAwait(false);
         }
 
-        /// <inheritdoc />
-        protected override bool IsFullLocalMetadata(Series item)
-        {
-            if (string.IsNullOrWhiteSpace(item.Overview))
-            {
-                return false;
-            }
-
-            if (!item.ProductionYear.HasValue)
-            {
-                return false;
-            }
-
-            return base.IsFullLocalMetadata(item);
-        }
-
         /// <inheritdoc />
         protected override void MergeData(MetadataResult<Series> source, MetadataResult<Series> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
         {

+ 3 - 2
tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Movies;
@@ -19,7 +20,7 @@ namespace Jellyfin.Providers.Tests.Manager
         [InlineData(true, true)]
         public void MergeBaseItemData_MergeMetadataSettings_MergesWhenSet(bool mergeMetadataSettings, bool defaultDate)
         {
-            var newLocked = new[] { MetadataField.Cast };
+            var newLocked = new[] { MetadataField.Genres, MetadataField.Cast };
             var newString = "new";
             var newDate = DateTime.Now;
 
@@ -77,7 +78,7 @@ namespace Jellyfin.Providers.Tests.Manager
 
         [Theory]
         [InlineData("Name", MetadataField.Name, false)]
-        [InlineData("OriginalTitle", null, false)]
+        [InlineData("OriginalTitle", null)]
         [InlineData("OfficialRating", MetadataField.OfficialRating)]
         [InlineData("CustomRating")]
         [InlineData("Tagline")]