< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Manager.MetadataService<T1, T2>
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Manager/MetadataService.cs
Line coverage
66%
Covered lines: 216
Uncovered lines: 107
Coverable lines: 323
Total lines: 1214
Line coverage: 66.8%
Branch coverage
65%
Covered branches: 211
Total branches: 320
Branch coverage: 65.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

File(s)

/srv/git/jellyfin/MediaBrowser.Providers/Manager/MetadataService.cs

#LineLine coverage
 1#nullable disable
 2
 3#pragma warning disable CS1591
 4
 5using System;
 6using System.Collections.Generic;
 7using System.Linq;
 8using System.Net.Http;
 9using System.Threading;
 10using System.Threading.Tasks;
 11using Jellyfin.Extensions;
 12using MediaBrowser.Controller.Configuration;
 13using MediaBrowser.Controller.Entities;
 14using MediaBrowser.Controller.Entities.Audio;
 15using MediaBrowser.Controller.Library;
 16using MediaBrowser.Controller.Providers;
 17using MediaBrowser.Model.Configuration;
 18using MediaBrowser.Model.Entities;
 19using MediaBrowser.Model.IO;
 20using MediaBrowser.Model.Providers;
 21using Microsoft.Extensions.Logging;
 22
 23namespace MediaBrowser.Providers.Manager
 24{
 25    public abstract class MetadataService<TItemType, TIdType> : IMetadataService
 26        where TItemType : BaseItem, IHasLookupInfo<TIdType>, new()
 27        where TIdType : ItemLookupInfo, new()
 28    {
 29        protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger<MetadataService<TItemT
 30        {
 57231            ServerConfigurationManager = serverConfigurationManager;
 57232            Logger = logger;
 57233            ProviderManager = providerManager;
 57234            FileSystem = fileSystem;
 57235            LibraryManager = libraryManager;
 57236            ImageProvider = new ItemImageProvider(Logger, ProviderManager, FileSystem);
 57237        }
 38
 39        protected ItemImageProvider ImageProvider { get; }
 40
 41        protected IServerConfigurationManager ServerConfigurationManager { get; }
 42
 43        protected ILogger<MetadataService<TItemType, TIdType>> Logger { get; }
 44
 45        protected IProviderManager ProviderManager { get; }
 46
 47        protected IFileSystem FileSystem { get; }
 48
 49        protected ILibraryManager LibraryManager { get; }
 50
 3651        protected virtual bool EnableUpdatingPremiereDateFromChildren => false;
 52
 3653        protected virtual bool EnableUpdatingGenresFromChildren => false;
 54
 3655        protected virtual bool EnableUpdatingStudiosFromChildren => false;
 56
 3657        protected virtual bool EnableUpdatingOfficialRatingFromChildren => false;
 58
 52859        public virtual int Order => 0;
 60
 61        private FileSystemMetadata TryGetFile(string path, IDirectoryService directoryService)
 62        {
 63            try
 64            {
 3665                return directoryService.GetFile(path);
 66            }
 067            catch (Exception ex)
 68            {
 069                Logger.LogError(ex, "Error getting file {Path}", path);
 070                return null;
 71            }
 3672        }
 73
 74        public virtual async Task<ItemUpdateType> RefreshMetadata(BaseItem item, MetadataRefreshOptions refreshOptions, 
 75        {
 76            var itemOfType = (TItemType)item;
 77
 78            var updateType = ItemUpdateType.None;
 79
 80            var libraryOptions = LibraryManager.GetLibraryOptions(item);
 81
 82            var requiresRefresh = libraryOptions.AutomaticRefreshIntervalDays > 0 && (DateTime.UtcNow - item.DateLastRef
 83
 84            if (!requiresRefresh && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
 85            {
 86                // TODO: If this returns true, should we instead just change metadata refresh mode to Full?
 87                requiresRefresh = item.RequiresRefresh();
 88
 89                if (requiresRefresh)
 90                {
 91                    Logger.LogDebug("Refreshing {Type} {Item} because item.RequiresRefresh() returned true", typeof(TIte
 92                }
 93            }
 94
 95            if (refreshOptions.RemoveOldMetadata && refreshOptions.ReplaceAllImages)
 96            {
 97                if (ImageProvider.RemoveImages(item))
 98                {
 99                    updateType |= ItemUpdateType.ImageUpdate;
 100                }
 101            }
 102
 103            var localImagesFailed = false;
 104            var allImageProviders = ProviderManager.GetImageProviders(item, refreshOptions).ToList();
 105
 106            // Only validate already registered images if we are replacing and saving locally
 107            if (item.IsSaveLocalMetadataEnabled() && refreshOptions.ReplaceAllImages)
 108            {
 109                item.ValidateImages();
 110            }
 111            else
 112            {
 113                // Run full image validation and register new local images
 114                try
 115                {
 116                    if (ImageProvider.ValidateImages(item, allImageProviders.OfType<ILocalImageProvider>(), refreshOptio
 117                    {
 118                        updateType |= ItemUpdateType.ImageUpdate;
 119                    }
 120                }
 121                catch (Exception ex)
 122                {
 123                    localImagesFailed = true;
 124                    Logger.LogError(ex, "Error validating images for {Item}", item.Path ?? item.Name ?? "Unknown name");
 125                }
 126            }
 127
 128            var metadataResult = new MetadataResult<TItemType>
 129            {
 130                Item = itemOfType,
 131                People = LibraryManager.GetPeople(item)
 132            };
 133
 134            bool hasRefreshedMetadata = true;
 135            bool hasRefreshedImages = true;
 136            var isFirstRefresh = item.DateLastRefreshed == default;
 137
 138            // Next run metadata providers
 139            if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
 140            {
 141                var providers = GetProviders(item, libraryOptions, refreshOptions, isFirstRefresh, requiresRefresh)
 142                    .ToList();
 143
 144                if (providers.Count > 0 || isFirstRefresh || requiresRefresh)
 145                {
 146                    if (item.BeforeMetadataRefresh(refreshOptions.ReplaceAllMetadata))
 147                    {
 148                        updateType |= ItemUpdateType.MetadataImport;
 149                    }
 150                }
 151
 152                if (providers.Count > 0)
 153                {
 154                    var id = itemOfType.GetLookupInfo();
 155
 156                    if (refreshOptions.SearchResult is not null)
 157                    {
 158                        ApplySearchResult(id, refreshOptions.SearchResult);
 159                    }
 160
 161                    id.IsAutomated = refreshOptions.IsAutomated;
 162
 163                    var hasMetadataSavers = ProviderManager.GetMetadataSavers(item, libraryOptions).Any();
 164                    var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, ImageProvider
 165
 166                    updateType |= result.UpdateType;
 167                    if (result.Failures > 0)
 168                    {
 169                        hasRefreshedMetadata = false;
 170                    }
 171                }
 172            }
 173
 174            // Next run remote image providers, but only if local image providers didn't throw an exception
 175            if (!localImagesFailed && refreshOptions.ImageRefreshMode > MetadataRefreshMode.ValidationOnly)
 176            {
 177                var providers = GetNonLocalImageProviders(item, allImageProviders, refreshOptions).ToList();
 178
 179                if (providers.Count > 0)
 180                {
 181                    var result = await ImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions
 182
 183                    updateType |= result.UpdateType;
 184                    if (result.Failures > 0)
 185                    {
 186                        hasRefreshedImages = false;
 187                    }
 188                }
 189            }
 190
 191            var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refresh
 192            updateType |= beforeSaveResult;
 193
 194            // Save if changes were made, or it's never been saved before
 195            if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || isFirstRefresh || refreshOptions.Replace
 196            {
 197                if (item.IsFileProtocol)
 198                {
 199                    var file = TryGetFile(item.Path, refreshOptions.DirectoryService);
 200                    if (file is not null)
 201                    {
 202                        item.DateModified = file.LastWriteTimeUtc;
 203                    }
 204                }
 205
 206                // If any of these properties are set then make sure the updateType is not None, just to force everythin
 207                if (refreshOptions.ForceSave || refreshOptions.ReplaceAllMetadata)
 208                {
 209                    updateType |= ItemUpdateType.MetadataDownload;
 210                }
 211
 212                if (hasRefreshedMetadata && hasRefreshedImages)
 213                {
 214                    item.DateLastRefreshed = DateTime.UtcNow;
 215                }
 216                else
 217                {
 218                    item.DateLastRefreshed = default;
 219                }
 220
 221                // Save to database
 222                await SaveItemAsync(metadataResult, updateType, cancellationToken).ConfigureAwait(false);
 223            }
 224
 225            await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false);
 226
 227            return updateType;
 228        }
 229
 230        private void ApplySearchResult(ItemLookupInfo lookupInfo, RemoteSearchResult result)
 231        {
 232            // Episode and Season do not support Identify, so the search results are the Series'
 233            switch (lookupInfo)
 234            {
 235                case EpisodeInfo episodeInfo:
 0236                    episodeInfo.SeriesProviderIds = result.ProviderIds;
 0237                    episodeInfo.ProviderIds.Clear();
 0238                    break;
 239                case SeasonInfo seasonInfo:
 0240                    seasonInfo.SeriesProviderIds = result.ProviderIds;
 0241                    seasonInfo.ProviderIds.Clear();
 0242                    break;
 243                default:
 0244                    lookupInfo.ProviderIds = result.ProviderIds;
 0245                    lookupInfo.Name = result.Name;
 0246                    lookupInfo.Year = result.ProductionYear;
 247                    break;
 248            }
 0249        }
 250
 251        protected async Task SaveItemAsync(MetadataResult<TItemType> result, ItemUpdateType reason, CancellationToken ca
 252        {
 253            if (result.Item.SupportsPeople)
 254            {
 255                var baseItem = result.Item;
 256
 257                await LibraryManager.UpdatePeopleAsync(baseItem, result.People, cancellationToken).ConfigureAwait(false)
 258            }
 259
 260            await result.Item.UpdateToRepositoryAsync(reason, cancellationToken).ConfigureAwait(false);
 261        }
 262
 263        protected virtual Task AfterMetadataRefresh(TItemType item, MetadataRefreshOptions refreshOptions, CancellationT
 264        {
 46265            item.AfterMetadataRefresh();
 46266            return Task.CompletedTask;
 267        }
 268
 269        /// <summary>
 270        /// Before the save.
 271        /// </summary>
 272        /// <param name="item">The item.</param>
 273        /// <param name="isFullRefresh">if set to <c>true</c> [is full refresh].</param>
 274        /// <param name="currentUpdateType">Type of the current update.</param>
 275        /// <returns>ItemUpdateType.</returns>
 276        private ItemUpdateType BeforeSave(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType)
 277        {
 46278            var updateType = BeforeSaveInternal(item, isFullRefresh, currentUpdateType);
 279
 46280            updateType |= item.OnMetadataChanged();
 281
 46282            return updateType;
 283        }
 284
 285        protected virtual ItemUpdateType BeforeSaveInternal(TItemType item, bool isFullRefresh, ItemUpdateType updateTyp
 286        {
 46287            if (EnableUpdateMetadataFromChildren(item, isFullRefresh, updateType))
 288            {
 0289                if (isFullRefresh || updateType > ItemUpdateType.None)
 290                {
 0291                    var children = GetChildrenForMetadataUpdates(item);
 292
 0293                    updateType = UpdateMetadataFromChildren(item, children, isFullRefresh, updateType);
 294                }
 295            }
 296
 46297            var presentationUniqueKey = item.CreatePresentationUniqueKey();
 46298            if (!string.Equals(item.PresentationUniqueKey, presentationUniqueKey, StringComparison.Ordinal))
 299            {
 35300                item.PresentationUniqueKey = presentationUniqueKey;
 35301                updateType |= ItemUpdateType.MetadataImport;
 302            }
 303
 46304            return updateType;
 305        }
 306
 307        protected virtual bool EnableUpdateMetadataFromChildren(TItemType item, bool isFullRefresh, ItemUpdateType curre
 308        {
 46309            if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
 310            {
 36311                if (EnableUpdatingPremiereDateFromChildren || EnableUpdatingGenresFromChildren || EnableUpdatingStudiosF
 312                {
 0313                    return true;
 314                }
 315
 36316                if (item is Folder folder)
 317                {
 36318                    return folder.SupportsDateLastMediaAdded || folder.SupportsCumulativeRunTimeTicks;
 319                }
 320            }
 321
 10322            return false;
 323        }
 324
 325        protected virtual IList<BaseItem> GetChildrenForMetadataUpdates(TItemType item)
 326        {
 0327            if (item is Folder folder)
 328            {
 0329                return folder.GetRecursiveChildren();
 330            }
 331
 0332            return Array.Empty<BaseItem>();
 333        }
 334
 335        protected virtual ItemUpdateType UpdateMetadataFromChildren(TItemType item, IList<BaseItem> children, bool isFul
 336        {
 0337            var updateType = ItemUpdateType.None;
 338
 0339            if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
 340            {
 0341                updateType |= UpdateCumulativeRunTimeTicks(item, children);
 0342                updateType |= UpdateDateLastMediaAdded(item, children);
 343
 344                // don't update user-changeable metadata for locked items
 0345                if (item.IsLocked)
 346                {
 0347                    return updateType;
 348                }
 349
 0350                if (EnableUpdatingPremiereDateFromChildren)
 351                {
 0352                    updateType |= UpdatePremiereDate(item, children);
 353                }
 354
 0355                if (EnableUpdatingGenresFromChildren)
 356                {
 0357                    updateType |= UpdateGenres(item, children);
 358                }
 359
 0360                if (EnableUpdatingStudiosFromChildren)
 361                {
 0362                    updateType |= UpdateStudios(item, children);
 363                }
 364
 0365                if (EnableUpdatingOfficialRatingFromChildren)
 366                {
 0367                    updateType |= UpdateOfficialRating(item, children);
 368                }
 369            }
 370
 0371            return updateType;
 372        }
 373
 374        private ItemUpdateType UpdateCumulativeRunTimeTicks(TItemType item, IList<BaseItem> children)
 375        {
 0376            if (item is Folder folder && folder.SupportsCumulativeRunTimeTicks)
 377            {
 0378                long ticks = 0;
 379
 0380                foreach (var child in children)
 381                {
 0382                    if (!child.IsFolder)
 383                    {
 0384                        ticks += child.RunTimeTicks ?? 0;
 385                    }
 386                }
 387
 0388                if (!folder.RunTimeTicks.HasValue || folder.RunTimeTicks.Value != ticks)
 389                {
 0390                    folder.RunTimeTicks = ticks;
 0391                    return ItemUpdateType.MetadataImport;
 392                }
 393            }
 394
 0395            return ItemUpdateType.None;
 396        }
 397
 398        private ItemUpdateType UpdateDateLastMediaAdded(TItemType item, IList<BaseItem> children)
 399        {
 0400            var updateType = ItemUpdateType.None;
 401
 0402            if (item is Folder folder && folder.SupportsDateLastMediaAdded)
 403            {
 0404                var dateLastMediaAdded = DateTime.MinValue;
 0405                var any = false;
 406
 0407                foreach (var child in children)
 408                {
 409                    // Exclude any folders and virtual items since they are only placeholders
 0410                    if (!child.IsFolder && !child.IsVirtualItem)
 411                    {
 0412                        var childDateCreated = child.DateCreated;
 0413                        if (childDateCreated > dateLastMediaAdded)
 414                        {
 0415                            dateLastMediaAdded = childDateCreated;
 416                        }
 417
 0418                        any = true;
 419                    }
 420                }
 421
 0422                if ((!folder.DateLastMediaAdded.HasValue && any) || folder.DateLastMediaAdded != dateLastMediaAdded)
 423                {
 0424                    folder.DateLastMediaAdded = dateLastMediaAdded;
 0425                    updateType = ItemUpdateType.MetadataImport;
 426                }
 427            }
 428
 0429            return updateType;
 430        }
 431
 432        private ItemUpdateType UpdatePremiereDate(TItemType item, IList<BaseItem> children)
 433        {
 0434            var updateType = ItemUpdateType.None;
 435
 0436            if (children.Count == 0)
 437            {
 0438                return updateType;
 439            }
 440
 0441            var date = children.Select(i => i.PremiereDate ?? DateTime.MaxValue).Min();
 442
 0443            var originalPremiereDate = item.PremiereDate;
 0444            var originalProductionYear = item.ProductionYear;
 445
 0446            if (date > DateTime.MinValue && date < DateTime.MaxValue)
 447            {
 0448                item.PremiereDate = date;
 0449                item.ProductionYear = date.Year;
 450            }
 451            else
 452            {
 0453                var year = children.Select(i => i.ProductionYear ?? 0).Min();
 454
 0455                if (year > 0)
 456                {
 0457                    item.ProductionYear = year;
 458                }
 459            }
 460
 0461            if ((originalPremiereDate ?? DateTime.MinValue) != (item.PremiereDate ?? DateTime.MinValue)
 0462                || (originalProductionYear ?? -1) != (item.ProductionYear ?? -1))
 463            {
 0464                updateType |= ItemUpdateType.MetadataEdit;
 465            }
 466
 0467            return updateType;
 468        }
 469
 470        private ItemUpdateType UpdateGenres(TItemType item, IList<BaseItem> children)
 471        {
 0472            var updateType = ItemUpdateType.None;
 473
 0474            if (!item.LockedFields.Contains(MetadataField.Genres))
 475            {
 0476                var currentList = item.Genres;
 477
 0478                item.Genres = children.SelectMany(i => i.Genres)
 0479                    .Distinct(StringComparer.OrdinalIgnoreCase)
 0480                    .ToArray();
 481
 0482                if (currentList.Length != item.Genres.Length || !currentList.Order().SequenceEqual(item.Genres.Order(), 
 483                {
 0484                    updateType |= ItemUpdateType.MetadataEdit;
 485                }
 486            }
 487
 0488            return updateType;
 489        }
 490
 491        private ItemUpdateType UpdateStudios(TItemType item, IList<BaseItem> children)
 492        {
 0493            var updateType = ItemUpdateType.None;
 494
 0495            if (!item.LockedFields.Contains(MetadataField.Studios))
 496            {
 0497                var currentList = item.Studios;
 498
 0499                item.Studios = children.SelectMany(i => i.Studios)
 0500                    .Distinct(StringComparer.OrdinalIgnoreCase)
 0501                    .ToArray();
 502
 0503                if (currentList.Length != item.Studios.Length || !currentList.Order().SequenceEqual(item.Studios.Order()
 504                {
 0505                    updateType |= ItemUpdateType.MetadataEdit;
 506                }
 507            }
 508
 0509            return updateType;
 510        }
 511
 512        private ItemUpdateType UpdateOfficialRating(TItemType item, IList<BaseItem> children)
 513        {
 0514            var updateType = ItemUpdateType.None;
 515
 0516            if (!item.LockedFields.Contains(MetadataField.OfficialRating))
 517            {
 0518                if (item.UpdateRatingToItems(children))
 519                {
 0520                    updateType |= ItemUpdateType.MetadataEdit;
 521                }
 522            }
 523
 0524            return updateType;
 525        }
 526
 527        /// <summary>
 528        /// Gets the providers.
 529        /// </summary>
 530        /// <param name="item">A media item.</param>
 531        /// <param name="libraryOptions">The LibraryOptions to use.</param>
 532        /// <param name="options">The MetadataRefreshOptions to use.</param>
 533        /// <param name="isFirstRefresh">Specifies first refresh mode.</param>
 534        /// <param name="requiresRefresh">Specifies refresh mode.</param>
 535        /// <returns>IEnumerable{`0}.</returns>
 536        protected IEnumerable<IMetadataProvider> GetProviders(BaseItem item, LibraryOptions libraryOptions, MetadataRefr
 537        {
 538            // Get providers to refresh
 46539            var providers = ProviderManager.GetMetadataProviders<TItemType>(item, libraryOptions).ToList();
 540
 46541            var metadataRefreshMode = options.MetadataRefreshMode;
 542
 543            // Run all if either of these flags are true
 46544            var runAllProviders = options.ReplaceAllMetadata ||
 46545                metadataRefreshMode == MetadataRefreshMode.FullRefresh ||
 46546                (isFirstRefresh && metadataRefreshMode >= MetadataRefreshMode.Default) ||
 46547                (requiresRefresh && metadataRefreshMode >= MetadataRefreshMode.Default);
 548
 46549            if (!runAllProviders)
 550            {
 10551                var providersWithChanges = providers
 10552                    .Where(i =>
 10553                    {
 10554                        if (i is IHasItemChangeMonitor hasFileChangeMonitor)
 10555                        {
 10556                            return HasChanged(item, hasFileChangeMonitor, options.DirectoryService);
 10557                        }
 10558
 10559                        return false;
 10560                    })
 10561                    .ToList();
 562
 10563                if (providersWithChanges.Count == 0)
 564                {
 0565                    providers = new List<IMetadataProvider<TItemType>>();
 566                }
 567                else
 568                {
 10569                    var anyRemoteProvidersChanged = providersWithChanges.OfType<IRemoteMetadataProvider>()
 10570                        .Any();
 571
 10572                    var anyLocalProvidersChanged = providersWithChanges.OfType<ILocalMetadataProvider>()
 10573                        .Any();
 574
 10575                    var anyLocalPreRefreshProvidersChanged = providersWithChanges.OfType<IPreRefreshProvider>()
 10576                        .Any();
 577
 10578                    providers = providers.Where(i =>
 10579                    {
 10580                        // If any provider reports a change, always run local ones as well
 10581                        if (i is ILocalMetadataProvider)
 10582                        {
 10583                            return anyRemoteProvidersChanged || anyLocalProvidersChanged || anyLocalPreRefreshProvidersC
 10584                        }
 10585
 10586                        // If any remote providers changed, run them all so that priorities can be honored
 10587                        if (i is IRemoteMetadataProvider)
 10588                        {
 10589                            if (options.MetadataRefreshMode == MetadataRefreshMode.ValidationOnly)
 10590                            {
 10591                                return false;
 10592                            }
 10593
 10594                            return anyRemoteProvidersChanged;
 10595                        }
 10596
 10597                        // Run custom refresh providers if they report a change or any remote providers change
 10598                        return anyRemoteProvidersChanged || providersWithChanges.Contains(i);
 10599                    }).ToList();
 600                }
 601            }
 602
 46603            return providers;
 604        }
 605
 606        protected virtual IEnumerable<IImageProvider> GetNonLocalImageProviders(BaseItem item, IEnumerable<IImageProvide
 607        {
 608            // Get providers to refresh
 46609            var providers = allImageProviders.Where(i => i is not ILocalImageProvider);
 610
 46611            var dateLastImageRefresh = item.DateLastRefreshed;
 612
 613            // Run all if either of these flags are true
 46614            var runAllProviders = options.ImageRefreshMode == MetadataRefreshMode.FullRefresh || dateLastImageRefresh ==
 615
 46616            if (!runAllProviders)
 617            {
 11618                providers = providers
 11619                    .Where(i =>
 11620                    {
 11621                        if (i is IHasItemChangeMonitor hasFileChangeMonitor)
 11622                        {
 11623                            return HasChanged(item, hasFileChangeMonitor, options.DirectoryService);
 11624                        }
 11625
 11626                        return false;
 11627                    });
 628            }
 629
 46630            return providers;
 631        }
 632
 633        public bool CanRefresh(BaseItem item)
 634        {
 1092635            return item is TItemType;
 636        }
 637
 638        public bool CanRefreshPrimary(Type type)
 639        {
 1168640            return type == typeof(TItemType);
 641        }
 642
 643        protected virtual async Task<RefreshResult> RefreshWithProviders(
 644            MetadataResult<TItemType> metadata,
 645            TIdType id,
 646            MetadataRefreshOptions options,
 647            ICollection<IMetadataProvider> providers,
 648            ItemImageProvider imageService,
 649            bool isSavingMetadata,
 650            CancellationToken cancellationToken)
 651        {
 652            var refreshResult = new RefreshResult
 653            {
 654                UpdateType = ItemUpdateType.None
 655            };
 656
 657            var item = metadata.Item;
 658
 659            var customProviders = providers.OfType<ICustomMetadataProvider<TItemType>>().ToList();
 660            var logName = !item.IsFileProtocol ? item.Name ?? item.Path : item.Path ?? item.Name;
 661
 662            foreach (var provider in customProviders.Where(i => i is IPreRefreshProvider))
 663            {
 664                await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwa
 665            }
 666
 667            if (item.IsLocked)
 668            {
 669                return refreshResult;
 670            }
 671
 672            var temp = new MetadataResult<TItemType>
 673            {
 674                Item = CreateNew()
 675            };
 676            temp.Item.Path = item.Path;
 677            temp.Item.Id = item.Id;
 678            temp.Item.PreferredMetadataCountryCode = item.PreferredMetadataCountryCode;
 679            temp.Item.PreferredMetadataLanguage = item.PreferredMetadataLanguage;
 680
 681            var foundImageTypes = new List<ImageType>();
 682
 683            // Do not execute local providers if we are identifying or replacing with local metadata saving enabled
 684            if (options.SearchResult is null && !(isSavingMetadata && options.ReplaceAllMetadata))
 685            {
 686                foreach (var provider in providers.OfType<ILocalMetadataProvider<TItemType>>())
 687                {
 688                    var providerName = provider.GetType().Name;
 689                    Logger.LogDebug("Running {Provider} for {Item}", providerName, logName);
 690
 691                    var itemInfo = new ItemInfo(item);
 692
 693                    try
 694                    {
 695                        var localItem = await provider.GetMetadata(itemInfo, options.DirectoryService, cancellationToken
 696
 697                        if (localItem.HasMetadata)
 698                        {
 699                            foreach (var remoteImage in localItem.RemoteImages)
 700                            {
 701                                try
 702                                {
 703                                    if (item.ImageInfos.Any(x => x.Type == remoteImage.Type)
 704                                        && !options.IsReplacingImage(remoteImage.Type))
 705                                    {
 706                                        continue;
 707                                    }
 708
 709                                    await ProviderManager.SaveImage(item, remoteImage.Url, remoteImage.Type, null, cance
 710                                    refreshResult.UpdateType |= ItemUpdateType.ImageUpdate;
 711
 712                                    // remember imagetype that has just been downloaded
 713                                    foundImageTypes.Add(remoteImage.Type);
 714                                }
 715                                catch (HttpRequestException ex)
 716                                {
 717                                    Logger.LogError(ex, "Could not save {ImageType} image: {Url}", Enum.GetName(remoteIm
 718                                }
 719                            }
 720
 721                            if (foundImageTypes.Count > 0)
 722                            {
 723                                imageService.UpdateReplaceImages(options, foundImageTypes);
 724                            }
 725
 726                            if (imageService.MergeImages(item, localItem.Images, options))
 727                            {
 728                                refreshResult.UpdateType |= ItemUpdateType.ImageUpdate;
 729                            }
 730
 731                            MergeData(localItem, temp, Array.Empty<MetadataField>(), false, true);
 732                            refreshResult.UpdateType |= ItemUpdateType.MetadataImport;
 733
 734                            break;
 735                        }
 736
 737                        Logger.LogDebug("{Provider} returned no metadata for {Item}", providerName, logName);
 738                    }
 739                    catch (OperationCanceledException)
 740                    {
 741                        throw;
 742                    }
 743                    catch (Exception ex)
 744                    {
 745                        Logger.LogError(ex, "Error in {Provider}", provider.Name);
 746
 747                        // If a local provider fails, consider that a failure
 748                        refreshResult.ErrorMessage = ex.Message;
 749                    }
 750                }
 751            }
 752
 753            var isLocalLocked = temp.Item.IsLocked;
 754            if (!isLocalLocked && (options.ReplaceAllMetadata || options.MetadataRefreshMode > MetadataRefreshMode.Valid
 755            {
 756                var remoteResult = await ExecuteRemoteProviders(temp, logName, false, id, providers.OfType<IRemoteMetada
 757                    .ConfigureAwait(false);
 758
 759                refreshResult.UpdateType |= remoteResult.UpdateType;
 760                refreshResult.ErrorMessage = remoteResult.ErrorMessage;
 761                refreshResult.Failures += remoteResult.Failures;
 762            }
 763
 764            if (providers.Any(i => i is not ICustomMetadataProvider))
 765            {
 766                if (refreshResult.UpdateType > ItemUpdateType.None)
 767                {
 768                    if (!options.RemoveOldMetadata)
 769                    {
 770                        // Add existing metadata to provider result if it does not exist there
 771                        MergeData(metadata, temp, Array.Empty<MetadataField>(), false, false);
 772                    }
 773
 774                    if (isLocalLocked)
 775                    {
 776                        MergeData(temp, metadata, item.LockedFields, true, true);
 777                    }
 778                    else
 779                    {
 780                        var shouldReplace = options.MetadataRefreshMode > MetadataRefreshMode.ValidationOnly || options.
 781                        MergeData(temp, metadata, item.LockedFields, shouldReplace, true);
 782                    }
 783                }
 784            }
 785
 786            foreach (var provider in customProviders.Where(i => i is not IPreRefreshProvider))
 787            {
 788                await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwa
 789            }
 790
 791            return refreshResult;
 792        }
 793
 794        private async Task RunCustomProvider(ICustomMetadataProvider<TItemType> provider, TItemType item, string logName
 795        {
 796            Logger.LogDebug("Running {Provider} for {Item}", provider.GetType().Name, logName);
 797
 798            try
 799            {
 800                refreshResult.UpdateType |= await provider.FetchAsync(item, options, cancellationToken).ConfigureAwait(f
 801            }
 802            catch (OperationCanceledException)
 803            {
 804                throw;
 805            }
 806            catch (Exception ex)
 807            {
 808                refreshResult.ErrorMessage = ex.Message;
 809                Logger.LogError(ex, "Error in {Provider}", provider.Name);
 810            }
 811        }
 812
 813        protected virtual TItemType CreateNew()
 814        {
 46815            return new TItemType();
 816        }
 817
 818        private async Task<RefreshResult> ExecuteRemoteProviders(MetadataResult<TItemType> temp, string logName, bool re
 819        {
 820            var refreshResult = new RefreshResult();
 821
 822            if (id is not null)
 823            {
 824                MergeNewData(temp.Item, id);
 825            }
 826
 827            foreach (var provider in providers)
 828            {
 829                var providerName = provider.GetType().Name;
 830                Logger.LogDebug("Running {Provider} for {Item}", providerName, logName);
 831
 832                try
 833                {
 834                    var result = await provider.GetMetadata(id, cancellationToken).ConfigureAwait(false);
 835
 836                    if (result.HasMetadata)
 837                    {
 838                        result.Provider = provider.Name;
 839
 840                        MergeData(result, temp, Array.Empty<MetadataField>(), replaceData, false);
 841                        MergeNewData(temp.Item, id);
 842
 843                        refreshResult.UpdateType |= ItemUpdateType.MetadataDownload;
 844                    }
 845                    else
 846                    {
 847                        Logger.LogDebug("{Provider} returned no metadata for {Item}", providerName, logName);
 848                    }
 849                }
 850                catch (OperationCanceledException)
 851                {
 852                    throw;
 853                }
 854                catch (Exception ex)
 855                {
 856                    refreshResult.Failures++;
 857                    refreshResult.ErrorMessage = ex.Message;
 858                    Logger.LogError(ex, "Error in {Provider}", provider.Name);
 859                }
 860            }
 861
 862            return refreshResult;
 863        }
 864
 865        private void MergeNewData(TItemType source, TIdType lookupInfo)
 866        {
 867            // Copy new provider id's that may have been obtained
 92868            foreach (var providerId in source.ProviderIds)
 869            {
 0870                var key = providerId.Key;
 871
 872                // Don't replace existing Id's.
 0873                lookupInfo.ProviderIds.TryAdd(key, providerId.Value);
 874            }
 46875        }
 876
 877        private bool HasChanged(BaseItem item, IHasItemChangeMonitor changeMonitor, IDirectoryService directoryService)
 878        {
 879            try
 880            {
 10881                var hasChanged = changeMonitor.HasChanged(item, directoryService);
 882
 10883                if (hasChanged)
 884                {
 10885                    Logger.LogDebug("{Monitor} reports change to {Item}", changeMonitor.GetType().Name, item.Path ?? ite
 886                }
 887
 10888                return hasChanged;
 889            }
 0890            catch (Exception ex)
 891            {
 0892                Logger.LogError(ex, "Error in {Monitor}.HasChanged", changeMonitor.GetType().Name);
 0893                return false;
 894            }
 10895        }
 896
 897        /// <summary>
 898        /// Merges metadata from source into target.
 899        /// </summary>
 900        /// <param name="source">The source for new metadata.</param>
 901        /// <param name="target">The target to insert new metadata into.</param>
 902        /// <param name="lockedFields">The fields that are locked and should not be updated.</param>
 903        /// <param name="replaceData"><c>true</c> if existing data should be replaced.</param>
 904        /// <param name="mergeMetadataSettings"><c>true</c> if the metadata settings in target should be updated to matc
 905        /// <exception cref="ArgumentException">Thrown if source or target are null.</exception>
 906        protected virtual void MergeData(
 907            MetadataResult<TItemType> source,
 908            MetadataResult<TItemType> target,
 909            MetadataField[] lockedFields,
 910            bool replaceData,
 911            bool mergeMetadataSettings)
 912        {
 0913            MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
 0914        }
 915
 916        internal static void MergeBaseItemData(
 917            MetadataResult<TItemType> sourceResult,
 918            MetadataResult<TItemType> targetResult,
 919            MetadataField[] lockedFields,
 920            bool replaceData,
 921            bool mergeMetadataSettings)
 922        {
 129923            var source = sourceResult.Item;
 129924            var target = targetResult.Item;
 925
 129926            ArgumentNullException.ThrowIfNull(sourceResult);
 129927            ArgumentNullException.ThrowIfNull(targetResult);
 928
 129929            if (!lockedFields.Contains(MetadataField.Name))
 930            {
 126931                if (replaceData || string.IsNullOrEmpty(target.Name))
 932                {
 933                    // Safeguard against incoming data having an empty name
 125934                    if (!string.IsNullOrWhiteSpace(source.Name))
 935                    {
 3936                        target.Name = source.Name;
 937                    }
 938                }
 939            }
 940
 129941            if (replaceData || string.IsNullOrEmpty(target.OriginalTitle))
 942            {
 128943                target.OriginalTitle = source.OriginalTitle;
 944            }
 945
 129946            if (replaceData || !target.CommunityRating.HasValue)
 947            {
 128948                target.CommunityRating = source.CommunityRating;
 949            }
 950
 129951            if (replaceData || !target.EndDate.HasValue)
 952            {
 128953                target.EndDate = source.EndDate;
 954            }
 955
 129956            if (!lockedFields.Contains(MetadataField.Genres))
 957            {
 127958                if (replaceData || target.Genres.Length == 0)
 959                {
 126960                    target.Genres = source.Genres;
 961                }
 962            }
 963
 129964            if (replaceData || !target.IndexNumber.HasValue)
 965            {
 128966                target.IndexNumber = source.IndexNumber;
 967            }
 968
 129969            if (!lockedFields.Contains(MetadataField.OfficialRating))
 970            {
 126971                if (replaceData || string.IsNullOrEmpty(target.OfficialRating))
 972                {
 125973                    target.OfficialRating = source.OfficialRating;
 974                }
 975            }
 976
 129977            if (replaceData || string.IsNullOrEmpty(target.CustomRating))
 978            {
 128979                target.CustomRating = source.CustomRating;
 980            }
 981
 129982            if (replaceData || string.IsNullOrEmpty(target.Tagline))
 983            {
 128984                target.Tagline = source.Tagline;
 985            }
 986
 129987            if (!lockedFields.Contains(MetadataField.Overview))
 988            {
 126989                if (replaceData || string.IsNullOrEmpty(target.Overview))
 990                {
 125991                    target.Overview = source.Overview;
 992                }
 993            }
 994
 129995            if (replaceData || !target.ParentIndexNumber.HasValue)
 996            {
 128997                target.ParentIndexNumber = source.ParentIndexNumber;
 998            }
 999
 1291000            if (!lockedFields.Contains(MetadataField.Cast))
 1001            {
 1281002                if (replaceData || targetResult.People is null || targetResult.People.Count == 0)
 1003                {
 1241004                    targetResult.People = sourceResult.People;
 1005                }
 41006                else if (sourceResult.People is not null && sourceResult.People.Count > 0)
 1007                {
 41008                    MergePeople(sourceResult.People, targetResult.People);
 1009                }
 1010            }
 1011
 1291012            if (replaceData || !target.PremiereDate.HasValue)
 1013            {
 1281014                target.PremiereDate = source.PremiereDate;
 1015            }
 1016
 1291017            if (replaceData || !target.ProductionYear.HasValue)
 1018            {
 1281019                target.ProductionYear = source.ProductionYear;
 1020            }
 1021
 1291022            if (!lockedFields.Contains(MetadataField.Runtime))
 1023            {
 1291024                if (replaceData || !target.RunTimeTicks.HasValue)
 1025                {
 1291026                    if (target is not Audio && target is not Video)
 1027                    {
 491028                        target.RunTimeTicks = source.RunTimeTicks;
 1029                    }
 1030                }
 1031            }
 1032
 1291033            if (!lockedFields.Contains(MetadataField.Studios))
 1034            {
 1271035                if (replaceData || target.Studios.Length == 0)
 1036                {
 1261037                    target.Studios = source.Studios;
 1038                }
 1039                else
 1040                {
 11041                    target.Studios = target.Studios.Concat(source.Studios).Distinct().ToArray();
 1042                }
 1043            }
 1044
 1291045            if (!lockedFields.Contains(MetadataField.Tags))
 1046            {
 1271047                if (replaceData || target.Tags.Length == 0)
 1048                {
 1261049                    target.Tags = source.Tags;
 1050                }
 1051                else
 1052                {
 11053                    target.Tags = target.Tags.Concat(source.Tags).Distinct().ToArray();
 1054                }
 1055            }
 1056
 1291057            if (!lockedFields.Contains(MetadataField.ProductionLocations))
 1058            {
 1271059                if (replaceData || target.ProductionLocations.Length == 0)
 1060                {
 1261061                    target.ProductionLocations = source.ProductionLocations;
 1062                }
 1063                else
 1064                {
 11065                    target.ProductionLocations = target.ProductionLocations.Concat(source.ProductionLocations).Distinct(
 1066                }
 1067            }
 1068
 2661069            foreach (var id in source.ProviderIds)
 1070            {
 41071                var key = id.Key;
 1072
 1073                // Don't replace existing Id's.
 41074                if (replaceData)
 1075                {
 11076                    target.ProviderIds[key] = id.Value;
 1077                }
 1078                else
 1079                {
 31080                    target.ProviderIds.TryAdd(key, id.Value);
 1081                }
 1082            }
 1083
 1291084            if (replaceData || !target.CriticRating.HasValue)
 1085            {
 1281086                target.CriticRating = source.CriticRating;
 1087            }
 1088
 1291089            if (replaceData || target.RemoteTrailers.Count == 0)
 1090            {
 1281091                target.RemoteTrailers = source.RemoteTrailers;
 1092            }
 1093            else
 1094            {
 11095                target.RemoteTrailers = target.RemoteTrailers.Concat(source.RemoteTrailers).DistinctBy(t => t.Url).ToArr
 1096            }
 1097
 1291098            MergeAlbumArtist(source, target, replaceData);
 1291099            MergeVideoInfo(source, target, replaceData);
 1291100            MergeDisplayOrder(source, target, replaceData);
 1101
 1291102            if (replaceData || string.IsNullOrEmpty(target.ForcedSortName))
 1103            {
 1281104                var forcedSortName = source.ForcedSortName;
 1281105                if (!string.IsNullOrEmpty(forcedSortName))
 1106                {
 31107                    target.ForcedSortName = forcedSortName;
 1108                }
 1109            }
 1110
 1291111            if (mergeMetadataSettings)
 1112            {
 21113                if (replaceData || !target.IsLocked)
 1114                {
 21115                    target.IsLocked = target.IsLocked || source.IsLocked;
 1116                }
 1117
 21118                if (target.LockedFields.Length == 0)
 1119                {
 01120                    target.LockedFields = source.LockedFields;
 1121                }
 1122                else
 1123                {
 21124                    target.LockedFields = target.LockedFields.Concat(source.LockedFields).Distinct().ToArray();
 1125                }
 1126
 21127                if (source.DateCreated != default)
 1128                {
 11129                    target.DateCreated = source.DateCreated;
 1130                }
 1131
 21132                if (replaceData || string.IsNullOrEmpty(target.PreferredMetadataCountryCode))
 1133                {
 21134                    target.PreferredMetadataCountryCode = source.PreferredMetadataCountryCode;
 1135                }
 1136
 21137                if (replaceData || string.IsNullOrEmpty(target.PreferredMetadataLanguage))
 1138                {
 21139                    target.PreferredMetadataLanguage = source.PreferredMetadataLanguage;
 1140                }
 1141            }
 1291142        }
 1143
 1144        private static void MergePeople(List<PersonInfo> source, List<PersonInfo> target)
 1145        {
 41146            if (target is null)
 1147            {
 01148                target = new List<PersonInfo>();
 1149            }
 1150
 161151            foreach (var person in target)
 1152            {
 41153                var normalizedName = person.Name.RemoveDiacritics();
 41154                var personInSource = source.FirstOrDefault(i => string.Equals(i.Name.RemoveDiacritics(), normalizedName,
 1155
 41156                if (personInSource is not null)
 1157                {
 101158                    foreach (var providerId in personInSource.ProviderIds)
 1159                    {
 21160                        person.ProviderIds.TryAdd(providerId.Key, providerId.Value);
 1161                    }
 1162
 31163                    if (string.IsNullOrWhiteSpace(person.ImageUrl))
 1164                    {
 21165                        person.ImageUrl = personInSource.ImageUrl;
 1166                    }
 1167                }
 1168            }
 41169        }
 1170
 1171        private static void MergeDisplayOrder(BaseItem source, BaseItem target, bool replaceData)
 1172        {
 1291173            if (source is IHasDisplayOrder sourceHasDisplayOrder
 1291174                && target is IHasDisplayOrder targetHasDisplayOrder)
 1175            {
 491176                if (replaceData || string.IsNullOrEmpty(targetHasDisplayOrder.DisplayOrder))
 1177                {
 481178                    var displayOrder = sourceHasDisplayOrder.DisplayOrder;
 481179                    if (!string.IsNullOrWhiteSpace(displayOrder))
 1180                    {
 31181                        targetHasDisplayOrder.DisplayOrder = displayOrder;
 1182                    }
 1183                }
 1184            }
 1291185        }
 1186
 1187        private static void MergeAlbumArtist(BaseItem source, BaseItem target, bool replaceData)
 1188        {
 1291189            if (source is IHasAlbumArtist sourceHasAlbumArtist
 1291190                && target is IHasAlbumArtist targetHasAlbumArtist)
 1191            {
 281192                if (replaceData || targetHasAlbumArtist.AlbumArtists.Count == 0)
 1193                {
 271194                    targetHasAlbumArtist.AlbumArtists = sourceHasAlbumArtist.AlbumArtists;
 1195                }
 11196                else if (sourceHasAlbumArtist.AlbumArtists.Count > 0)
 1197                {
 11198                    targetHasAlbumArtist.AlbumArtists = targetHasAlbumArtist.AlbumArtists.Concat(sourceHasAlbumArtist.Al
 1199                }
 1200            }
 1021201        }
 1202
 1203        private static void MergeVideoInfo(BaseItem source, BaseItem target, bool replaceData)
 1204        {
 1291205            if (source is Video sourceCast && target is Video targetCast)
 1206            {
 521207                if (replaceData || !targetCast.Video3DFormat.HasValue)
 1208                {
 511209                    targetCast.Video3DFormat = sourceCast.Video3DFormat;
 1210                }
 1211            }
 1291212        }
 1213    }
 1214}

Methods/Properties

.ctor(MediaBrowser.Controller.Configuration.IServerConfigurationManager,Microsoft.Extensions.Logging.ILogger`1<MediaBrowser.Providers.Manager.MetadataService`2<TItemType,TIdType>>,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Controller.Library.ILibraryManager)
get_EnableUpdatingPremiereDateFromChildren()
get_EnableUpdatingGenresFromChildren()
get_EnableUpdatingStudiosFromChildren()
get_EnableUpdatingOfficialRatingFromChildren()
get_Order()
TryGetFile(System.String,MediaBrowser.Controller.Providers.IDirectoryService)
ApplySearchResult(MediaBrowser.Controller.Providers.ItemLookupInfo,MediaBrowser.Model.Providers.RemoteSearchResult)
AfterMetadataRefresh(TItemType,MediaBrowser.Controller.Providers.MetadataRefreshOptions,System.Threading.CancellationToken)
BeforeSave(TItemType,System.Boolean,MediaBrowser.Controller.Library.ItemUpdateType)
BeforeSaveInternal(TItemType,System.Boolean,MediaBrowser.Controller.Library.ItemUpdateType)
EnableUpdateMetadataFromChildren(TItemType,System.Boolean,MediaBrowser.Controller.Library.ItemUpdateType)
GetChildrenForMetadataUpdates(TItemType)
UpdateMetadataFromChildren(TItemType,System.Collections.Generic.IList`1<MediaBrowser.Controller.Entities.BaseItem>,System.Boolean,MediaBrowser.Controller.Library.ItemUpdateType)
UpdateCumulativeRunTimeTicks(TItemType,System.Collections.Generic.IList`1<MediaBrowser.Controller.Entities.BaseItem>)
UpdateDateLastMediaAdded(TItemType,System.Collections.Generic.IList`1<MediaBrowser.Controller.Entities.BaseItem>)
UpdatePremiereDate(TItemType,System.Collections.Generic.IList`1<MediaBrowser.Controller.Entities.BaseItem>)
UpdateGenres(TItemType,System.Collections.Generic.IList`1<MediaBrowser.Controller.Entities.BaseItem>)
UpdateStudios(TItemType,System.Collections.Generic.IList`1<MediaBrowser.Controller.Entities.BaseItem>)
UpdateOfficialRating(TItemType,System.Collections.Generic.IList`1<MediaBrowser.Controller.Entities.BaseItem>)
GetProviders(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Model.Configuration.LibraryOptions,MediaBrowser.Controller.Providers.MetadataRefreshOptions,System.Boolean,System.Boolean)
GetNonLocalImageProviders(MediaBrowser.Controller.Entities.BaseItem,System.Collections.Generic.IEnumerable`1<MediaBrowser.Controller.Providers.IImageProvider>,MediaBrowser.Controller.Providers.ImageRefreshOptions)
CanRefresh(MediaBrowser.Controller.Entities.BaseItem)
CanRefreshPrimary(System.Type)
CreateNew()
MergeNewData(TItemType,TIdType)
HasChanged(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Providers.IHasItemChangeMonitor,MediaBrowser.Controller.Providers.IDirectoryService)
MergeData(MediaBrowser.Controller.Providers.MetadataResult`1<TItemType>,MediaBrowser.Controller.Providers.MetadataResult`1<TItemType>,MediaBrowser.Model.Entities.MetadataField[],System.Boolean,System.Boolean)
MergeBaseItemData(MediaBrowser.Controller.Providers.MetadataResult`1<TItemType>,MediaBrowser.Controller.Providers.MetadataResult`1<TItemType>,MediaBrowser.Model.Entities.MetadataField[],System.Boolean,System.Boolean)
MergePeople(System.Collections.Generic.List`1<MediaBrowser.Controller.Entities.PersonInfo>,System.Collections.Generic.List`1<MediaBrowser.Controller.Entities.PersonInfo>)
MergeDisplayOrder(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Entities.BaseItem,System.Boolean)
MergeAlbumArtist(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Entities.BaseItem,System.Boolean)
MergeVideoInfo(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Entities.BaseItem,System.Boolean)