< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Manager.ItemImageProvider
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Manager/ItemImageProvider.cs
Line coverage
87%
Covered lines: 109
Uncovered lines: 15
Coverable lines: 124
Total lines: 700
Line coverage: 87.9%
Branch coverage
81%
Covered branches: 67
Total branches: 82
Branch coverage: 81.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
RemoveImages(...)87.5%88100%
ValidateImages(...)83.33%66100%
ContainsImages(...)100%1212100%
PruneImages(...)70%25.181046.66%
UpdateReplaceImages(...)75%4.59466.66%
MergeImages(...)86.36%22.042295.83%
GetFirstLocalImageInfoByType(...)100%44100%
UpdateMultiImages(...)100%22100%
EnableImageStub(...)50%24.651255.55%
SaveImageStub(...)100%22100%
SaveImageStub(...)100%11100%

File(s)

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

#LineLine coverage
 1#nullable disable
 2
 3using System;
 4using System.Collections.Generic;
 5using System.IO;
 6using System.Linq;
 7using System.Net;
 8using System.Net.Http;
 9using System.Threading;
 10using System.Threading.Tasks;
 11using MediaBrowser.Controller.Entities;
 12using MediaBrowser.Controller.Entities.Audio;
 13using MediaBrowser.Controller.Entities.TV;
 14using MediaBrowser.Controller.Library;
 15using MediaBrowser.Controller.LiveTv;
 16using MediaBrowser.Controller.Providers;
 17using MediaBrowser.Model.Configuration;
 18using MediaBrowser.Model.Drawing;
 19using MediaBrowser.Model.Entities;
 20using MediaBrowser.Model.IO;
 21using MediaBrowser.Model.MediaInfo;
 22using MediaBrowser.Model.Net;
 23using MediaBrowser.Model.Providers;
 24using Microsoft.Extensions.Logging;
 25
 26namespace MediaBrowser.Providers.Manager
 27{
 28    /// <summary>
 29    /// Utilities for managing images attached to items.
 30    /// </summary>
 31    public class ItemImageProvider
 32    {
 33        private readonly ILogger _logger;
 34        private readonly IProviderManager _providerManager;
 35        private readonly IFileSystem _fileSystem;
 236        private static readonly ImageType[] AllImageTypes = Enum.GetValues<ImageType>();
 37
 38        /// <summary>
 39        /// Image types that are only one per item.
 40        /// </summary>
 241        private static readonly ImageType[] _singularImages =
 242        {
 243            ImageType.Primary,
 244            ImageType.Art,
 245            ImageType.Banner,
 246            ImageType.Box,
 247            ImageType.BoxRear,
 248            ImageType.Disc,
 249            ImageType.Logo,
 250            ImageType.Menu,
 251            ImageType.Thumb
 252        };
 53
 54        /// <summary>
 55        /// Initializes a new instance of the <see cref="ItemImageProvider"/> class.
 56        /// </summary>
 57        /// <param name="logger">The logger.</param>
 58        /// <param name="providerManager">The provider manager for interacting with provider image references.</param>
 59        /// <param name="fileSystem">The filesystem.</param>
 60        public ItemImageProvider(ILogger logger, IProviderManager providerManager, IFileSystem fileSystem)
 61        {
 61762            _logger = logger;
 61763            _providerManager = providerManager;
 61764            _fileSystem = fileSystem;
 61765        }
 66
 67        /// <summary>
 68        /// Removes all existing images from the provided item.
 69        /// </summary>
 70        /// <param name="item">The <see cref="BaseItem"/> to remove images from.</param>
 71        /// <param name="canDeleteLocal">Whether removing images outside metadata folder is allowed.</param>
 72        /// <returns><c>true</c> if changes were made to the item; otherwise <c>false</c>.</returns>
 73        public bool RemoveImages(BaseItem item, bool canDeleteLocal = false)
 74        {
 375            var singular = new List<ItemImageInfo>();
 376            var itemMetadataPath = item.GetInternalMetadataPath();
 6077            for (var i = 0; i < _singularImages.Length; i++)
 78            {
 2779                var currentImage = item.GetImageInfo(_singularImages[i], 0);
 2780                if (currentImage is not null)
 81                {
 182                    var imageInMetadataFolder = currentImage.Path.StartsWith(itemMetadataPath, StringComparison.OrdinalI
 183                    if (imageInMetadataFolder || canDeleteLocal || item.IsSaveLocalMetadataEnabled())
 84                    {
 185                        singular.Add(currentImage);
 86                    }
 87                }
 88            }
 89
 390            singular.AddRange(item.GetImages(ImageType.Backdrop));
 391            PruneImages(item, singular);
 92
 393            return singular.Count > 0;
 94        }
 95
 96        /// <summary>
 97        /// Verifies existing images have valid paths and adds any new local images provided.
 98        /// </summary>
 99        /// <param name="item">The <see cref="BaseItem"/> to validate images for.</param>
 100        /// <param name="providers">The providers to use, must include <see cref="ILocalImageProvider"/>(s) for local sc
 101        /// <param name="refreshOptions">The refresh options.</param>
 102        /// <returns><c>true</c> if changes were made to the item; otherwise <c>false</c>.</returns>
 103        public bool ValidateImages(BaseItem item, IEnumerable<IImageProvider> providers, ImageRefreshOptions refreshOpti
 104        {
 54105            var hasChanges = false;
 54106            var directoryService = refreshOptions?.DirectoryService;
 107
 54108            if (item is not Photo)
 109            {
 53110                var images = providers.OfType<ILocalImageProvider>()
 53111                    .SelectMany(i => i.GetImages(item, directoryService))
 53112                    .ToList();
 113
 53114                if (MergeImages(item, images, refreshOptions))
 115                {
 4116                    hasChanges = true;
 117                }
 118            }
 119
 54120            return hasChanges;
 121        }
 122
 123        /// <summary>
 124        /// Refreshes from the providers according to the given options.
 125        /// </summary>
 126        /// <param name="item">The <see cref="BaseItem"/> to gather images for.</param>
 127        /// <param name="libraryOptions">The library options.</param>
 128        /// <param name="providers">The providers to query for images.</param>
 129        /// <param name="refreshOptions">The refresh options.</param>
 130        /// <param name="cancellationToken">The cancellation token.</param>
 131        /// <returns>The refresh result.</returns>
 132        public async Task<RefreshResult> RefreshImages(
 133            BaseItem item,
 134            LibraryOptions libraryOptions,
 135            IEnumerable<IImageProvider> providers,
 136            ImageRefreshOptions refreshOptions,
 137            CancellationToken cancellationToken)
 138        {
 139            var oldBackdropImages = Array.Empty<ItemImageInfo>();
 140            if (refreshOptions.IsReplacingImage(ImageType.Backdrop))
 141            {
 142                oldBackdropImages = item.GetImages(ImageType.Backdrop).ToArray();
 143            }
 144
 145            var result = new RefreshResult { UpdateType = ItemUpdateType.None };
 146
 147            var typeName = item.GetType().Name;
 148            var typeOptions = libraryOptions.GetTypeOptions(typeName) ?? new TypeOptions { Type = typeName };
 149
 150            // track library limits, adding buffer to allow lazy replacing of current images
 151            var backdropLimit = typeOptions.GetLimit(ImageType.Backdrop) + oldBackdropImages.Length;
 152            var downloadedImages = new List<ImageType>();
 153
 154            foreach (var provider in providers)
 155            {
 156                if (provider is IRemoteImageProvider remoteProvider)
 157                {
 158                    await RefreshFromProvider(item, remoteProvider, refreshOptions, typeOptions, backdropLimit, download
 159                    continue;
 160                }
 161
 162                if (provider is IDynamicImageProvider dynamicImageProvider)
 163                {
 164                    await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, typeOptions, downloadedImages,
 165                }
 166            }
 167
 168            // Only delete existing multi-images if new ones were added
 169            if (oldBackdropImages.Length > 0 && oldBackdropImages.Length < item.GetImages(ImageType.Backdrop).Count())
 170            {
 171                PruneImages(item, oldBackdropImages);
 172            }
 173
 174            return result;
 175        }
 176
 177        /// <summary>
 178        /// Refreshes from a dynamic provider.
 179        /// </summary>
 180        private async Task RefreshFromProvider(
 181            BaseItem item,
 182            IDynamicImageProvider provider,
 183            ImageRefreshOptions refreshOptions,
 184            TypeOptions savedOptions,
 185            List<ImageType> downloadedImages,
 186            RefreshResult result,
 187            CancellationToken cancellationToken)
 188        {
 189            try
 190            {
 191                var images = provider.GetSupportedImages(item);
 192
 193                foreach (var imageType in images)
 194                {
 195                    if (!savedOptions.IsEnabled(imageType))
 196                    {
 197                        continue;
 198                    }
 199
 200                    if (!item.HasImage(imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Co
 201                    {
 202                        _logger.LogDebug("Running {Provider} for {Item}", provider.GetType().Name, item.Path ?? item.Nam
 203
 204                        var response = await provider.GetImage(item, imageType, cancellationToken).ConfigureAwait(false)
 205
 206                        if (response.HasImage)
 207                        {
 208                            if (string.IsNullOrEmpty(response.Path))
 209                            {
 210                                var mimeType = response.Format.GetMimeType();
 211
 212                                await _providerManager.SaveImage(item, response.Stream, mimeType, imageType, null, cance
 213                            }
 214                            else
 215                            {
 216                                if (response.Protocol == MediaProtocol.Http)
 217                                {
 218                                    _logger.LogDebug("Setting image url into item {Item}", item.Id);
 219                                    var index = item.AllowsMultipleImages(imageType) ? item.GetImages(imageType).Count()
 220                                    item.SetImage(
 221                                        new ItemImageInfo
 222                                        {
 223                                            Path = response.Path,
 224                                            Type = imageType
 225                                        },
 226                                        index);
 227                                }
 228                                else
 229                                {
 230                                    var mimeType = MimeTypes.GetMimeType(response.Path);
 231
 232                                    var stream = AsyncFile.OpenRead(response.Path);
 233
 234                                    await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellati
 235                                }
 236                            }
 237
 238                            downloadedImages.Add(imageType);
 239                            result.UpdateType |= ItemUpdateType.ImageUpdate;
 240                        }
 241                    }
 242                }
 243            }
 244            catch (OperationCanceledException)
 245            {
 246                throw;
 247            }
 248            catch (Exception ex)
 249            {
 250                result.ErrorMessage = ex.Message;
 251                _logger.LogError(ex, "Error in {Provider}", provider.Name);
 252            }
 253        }
 254
 255        /// <summary>
 256        /// Refreshes from a remote provider.
 257        /// </summary>
 258        /// <param name="item">The item.</param>
 259        /// <param name="provider">The provider.</param>
 260        /// <param name="refreshOptions">The refresh options.</param>
 261        /// <param name="savedOptions">The saved options.</param>
 262        /// <param name="backdropLimit">The backdrop limit.</param>
 263        /// <param name="downloadedImages">The downloaded images.</param>
 264        /// <param name="result">The result.</param>
 265        /// <param name="cancellationToken">The cancellation token.</param>
 266        /// <returns>Task.</returns>
 267        private async Task RefreshFromProvider(
 268            BaseItem item,
 269            IRemoteImageProvider provider,
 270            ImageRefreshOptions refreshOptions,
 271            TypeOptions savedOptions,
 272            int backdropLimit,
 273            List<ImageType> downloadedImages,
 274            RefreshResult result,
 275            CancellationToken cancellationToken)
 276        {
 277            try
 278            {
 279                if (!item.SupportsRemoteImageDownloading)
 280                {
 281                    return;
 282                }
 283
 284                if (!refreshOptions.ReplaceAllImages &&
 285                    refreshOptions.ReplaceImages.Count == 0 &&
 286                    ContainsImages(item, provider.GetSupportedImages(item).ToList(), savedOptions, backdropLimit))
 287                {
 288                    return;
 289                }
 290
 291                _logger.LogDebug("Running {Provider} for {Item}", provider.GetType().Name, item.Path ?? item.Name);
 292
 293                var images = await _providerManager.GetAvailableRemoteImages(
 294                    item,
 295                    new RemoteImageQuery(provider.Name)
 296                    {
 297                        IncludeAllLanguages = true,
 298                        IncludeDisabledProviders = false,
 299                    },
 300                    cancellationToken).ConfigureAwait(false);
 301
 302                var list = images.ToList();
 303                int minWidth;
 304
 305                foreach (var imageType in _singularImages)
 306                {
 307                    if (!savedOptions.IsEnabled(imageType))
 308                    {
 309                        continue;
 310                    }
 311
 312                    if (!item.HasImage(imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Co
 313                    {
 314                        minWidth = savedOptions.GetMinWidth(imageType);
 315                        var downloaded = await DownloadImage(item, provider, result, list, minWidth, imageType, cancella
 316
 317                        if (downloaded)
 318                        {
 319                            downloadedImages.Add(imageType);
 320                        }
 321                    }
 322                }
 323
 324                minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
 325                var listWithNoLangFirst = list.OrderByDescending(i => string.IsNullOrEmpty(i.Language));
 326                await DownloadMultiImages(item, ImageType.Backdrop, refreshOptions, backdropLimit, provider, result, lis
 327            }
 328            catch (OperationCanceledException)
 329            {
 330                throw;
 331            }
 332            catch (Exception ex)
 333            {
 334                result.ErrorMessage = ex.Message;
 335                _logger.LogError(ex, "Error in {Provider}", provider.Name);
 336            }
 337        }
 338
 339        /// <summary>
 340        /// Determines if an item already contains the given images.
 341        /// </summary>
 342        /// <param name="item">The item.</param>
 343        /// <param name="images">The images.</param>
 344        /// <param name="savedOptions">The saved options.</param>
 345        /// <param name="backdropLimit">The backdrop limit.</param>
 346        /// <returns><c>true</c> if the specified item contains images; otherwise, <c>false</c>.</returns>
 347        private bool ContainsImages(BaseItem item, List<ImageType> images, TypeOptions savedOptions, int backdropLimit)
 348        {
 349            // Using .Any causes the creation of a DisplayClass aka. variable capture
 130350            for (var i = 0; i < _singularImages.Length; i++)
 351            {
 59352                var type = _singularImages[i];
 59353                if (images.Contains(type) && !item.HasImage(type) && savedOptions.GetLimit(type) > 0)
 354                {
 5355                    return false;
 356                }
 357            }
 358
 6359            if (images.Contains(ImageType.Backdrop) && item.GetImages(ImageType.Backdrop).Count() < backdropLimit)
 360            {
 3361                return false;
 362            }
 363
 3364            return true;
 365        }
 366
 367        private void PruneImages(BaseItem item, IReadOnlyList<ItemImageInfo> images)
 368        {
 32369            foreach (var image in images)
 370            {
 9371                if (image.IsLocalFile)
 372                {
 373                    try
 374                    {
 9375                        _fileSystem.DeleteFile(image.Path);
 9376                    }
 0377                    catch (FileNotFoundException)
 378                    {
 379                        // Nothing to do, already gone
 0380                    }
 0381                    catch (UnauthorizedAccessException ex)
 382                    {
 0383                        _logger.LogWarning(ex, "Unable to delete {Image}", image.Path);
 0384                    }
 385                }
 386            }
 387
 7388            item.RemoveImages(images);
 389
 390            // Cleanup old metadata directory for episodes if empty
 7391            if (item is Episode)
 392            {
 0393                var oldLocalMetadataDirectory = Path.Combine(item.ContainingFolderPath, "metadata");
 0394                if (_fileSystem.DirectoryExists(oldLocalMetadataDirectory) && !_fileSystem.GetFiles(oldLocalMetadataDire
 395                {
 0396                    Directory.Delete(oldLocalMetadataDirectory);
 397                }
 398            }
 7399        }
 400
 401        /// <summary>
 402        /// Merges a list of images into the provided item, validating existing images and replacing them or adding new 
 403        /// </summary>
 404        /// <param name="refreshOptions">The refresh options.</param>
 405        /// <param name="dontReplaceImages">List of imageTypes to remove from ReplaceImages.</param>
 406        public void UpdateReplaceImages(ImageRefreshOptions refreshOptions, ICollection<ImageType> dontReplaceImages)
 407        {
 3408            if (refreshOptions is not null)
 409            {
 2410                if (refreshOptions.ReplaceAllImages)
 411                {
 0412                    refreshOptions.ReplaceAllImages = false;
 0413                    refreshOptions.ReplaceImages = AllImageTypes.ToList();
 414                }
 415
 2416                refreshOptions.ReplaceImages = refreshOptions.ReplaceImages.Except(dontReplaceImages).ToList();
 417            }
 3418        }
 419
 420        /// <summary>
 421        /// Merges a list of images into the provided item, validating existing images and replacing them or adding new 
 422        /// </summary>
 423        /// <param name="item">The <see cref="BaseItem"/> to modify.</param>
 424        /// <param name="images">The new images to place in <c>item</c>.</param>
 425        /// <param name="refreshOptions">The refresh options.</param>
 426        /// <returns><c>true</c> if changes were made to the item; otherwise <c>false</c>.</returns>
 427        public bool MergeImages(BaseItem item, IReadOnlyList<LocalImageInfo> images, ImageRefreshOptions refreshOptions)
 428        {
 60429            var changed = item.ValidateImages();
 60430            var foundImageTypes = new List<ImageType>();
 1200431            for (var i = 0; i < _singularImages.Length; i++)
 432            {
 540433                var type = _singularImages[i];
 540434                var image = GetFirstLocalImageInfoByType(images, type);
 540435                if (image is not null)
 436                {
 4437                    var currentImage = item.GetImageInfo(type, 0);
 438                    // if image file is stored with media, don't replace that later
 4439                    if (item.ContainingFolderPath is not null && item.ContainingFolderPath.Contains(Path.GetDirectoryNam
 440                    {
 0441                        foundImageTypes.Add(type);
 442                    }
 443
 4444                    if (currentImage is null || !string.Equals(currentImage.Path, image.FileInfo.FullName, StringCompari
 445                    {
 2446                        item.SetImagePath(type, image.FileInfo);
 2447                        changed = true;
 448                    }
 449                    else
 450                    {
 2451                        var newDateModified = _fileSystem.GetLastWriteTimeUtc(image.FileInfo);
 452
 453                        // If date changed then we need to reset saved image dimensions
 2454                        if (currentImage.DateModified != newDateModified && (currentImage.Width > 0 || currentImage.Heig
 455                        {
 1456                            currentImage.Width = 0;
 1457                            currentImage.Height = 0;
 1458                            changed = true;
 459                        }
 460
 2461                        currentImage.DateModified = newDateModified;
 462                    }
 463                }
 464            }
 465
 60466            if (UpdateMultiImages(item, images, ImageType.Backdrop))
 467            {
 3468                changed = true;
 3469                foundImageTypes.Add(ImageType.Backdrop);
 470            }
 471
 60472            if (foundImageTypes.Count > 0)
 473            {
 3474                UpdateReplaceImages(refreshOptions, foundImageTypes);
 475            }
 476
 60477            return changed;
 478        }
 479
 480        private static LocalImageInfo GetFirstLocalImageInfoByType(IReadOnlyList<LocalImageInfo> images, ImageType type)
 481        {
 540482            var len = images.Count;
 1288483            for (var i = 0; i < len; i++)
 484            {
 108485                var image = images[i];
 108486                if (image.Type == type)
 487                {
 4488                    return image;
 489                }
 490            }
 491
 536492            return null;
 493        }
 494
 495        private bool UpdateMultiImages(BaseItem item, IReadOnlyList<LocalImageInfo> images, ImageType type)
 496        {
 60497            var changed = false;
 498
 60499            var newImageFileInfos = images
 60500                .Where(i => i.Type == type)
 60501                .Select(i => i.FileInfo)
 60502                .ToList();
 503
 60504            if (item.AddImages(type, newImageFileInfos))
 505            {
 3506                changed = true;
 507            }
 508
 60509            return changed;
 510        }
 511
 512        private async Task<bool> DownloadImage(
 513            BaseItem item,
 514            IRemoteImageProvider provider,
 515            RefreshResult result,
 516            IEnumerable<RemoteImageInfo> images,
 517            int minWidth,
 518            ImageType type,
 519            CancellationToken cancellationToken)
 520        {
 521            var eligibleImages = images
 522                .Where(i => i.Type == type && (i.Width is null || i.Width >= minWidth))
 523                .ToList();
 524
 525            if (EnableImageStub(item) && eligibleImages.Count > 0)
 526            {
 527                SaveImageStub(item, type, eligibleImages.Select(i => i.Url));
 528                result.UpdateType |= ItemUpdateType.ImageUpdate;
 529                return true;
 530            }
 531
 532            foreach (var image in eligibleImages)
 533            {
 534                var url = image.Url;
 535
 536                try
 537                {
 538                    using var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);
 539
 540                    // Sometimes providers send back bad urls. Just move to the next image
 541                    if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.Forbidde
 542                    {
 543                        _logger.LogDebug("{Url} returned {StatusCode}, ignoring", url, response.StatusCode);
 544                        continue;
 545                    }
 546
 547                    if (!response.IsSuccessStatusCode)
 548                    {
 549                        _logger.LogWarning("{Url} returned {StatusCode}, skipping all remaining requests", url, response
 550                        break;
 551                    }
 552
 553                    var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
 554                    await using (stream.ConfigureAwait(false))
 555                    {
 556                        await _providerManager.SaveImage(
 557                            item,
 558                            stream,
 559                            response.Content.Headers.ContentType?.MediaType,
 560                            type,
 561                            null,
 562                            cancellationToken).ConfigureAwait(false);
 563                    }
 564
 565                    result.UpdateType |= ItemUpdateType.ImageUpdate;
 566                    return true;
 567                }
 568                catch (HttpRequestException)
 569                {
 570                    break;
 571                }
 572            }
 573
 574            return false;
 575        }
 576
 577        private bool EnableImageStub(BaseItem item)
 578        {
 134579            if (item is LiveTvProgram)
 580            {
 0581                return true;
 582            }
 583
 134584            if (!item.IsFileProtocol)
 585            {
 95586                return true;
 587            }
 588
 39589            if (item is IItemByName and not MusicArtist)
 590            {
 0591                var hasDualAccess = item as IHasDualAccess;
 0592                if (hasDualAccess is null || hasDualAccess.IsAccessedByName)
 593                {
 0594                    return true;
 595                }
 596            }
 597
 598            // We always want to use prefetched images
 39599            return false;
 600        }
 601
 602        private void SaveImageStub(BaseItem item, ImageType imageType, IEnumerable<string> urls)
 603        {
 9604            var newIndex = item.AllowsMultipleImages(imageType) ? item.GetImages(imageType).Count() : 0;
 605
 9606            SaveImageStub(item, imageType, urls, newIndex);
 9607        }
 608
 609        private void SaveImageStub(BaseItem item, ImageType imageType, IEnumerable<string> urls, int newIndex)
 610        {
 9611            var path = string.Join('|', urls.Take(1));
 612
 9613            item.SetImage(
 9614                new ItemImageInfo
 9615                {
 9616                    Path = path,
 9617                    Type = imageType
 9618                },
 9619                newIndex);
 9620        }
 621
 622        private async Task DownloadMultiImages(BaseItem item, ImageType imageType, ImageRefreshOptions refreshOptions, i
 623        {
 624            foreach (var image in images.Where(i => i.Type == imageType))
 625            {
 626                if (item.GetImages(imageType).Count() >= limit)
 627                {
 628                    break;
 629                }
 630
 631                if (image.Width.HasValue && image.Width.Value < minWidth)
 632                {
 633                    continue;
 634                }
 635
 636                var url = image.Url;
 637
 638                if (EnableImageStub(item))
 639                {
 640                    SaveImageStub(item, imageType, new[] { url });
 641                    result.UpdateType |= ItemUpdateType.ImageUpdate;
 642                    continue;
 643                }
 644
 645                try
 646                {
 647                    using var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);
 648
 649                    // Sometimes providers send back bad urls. Just move to the next image
 650                    if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.Forbidde
 651                    {
 652                        _logger.LogDebug("{Url} returned {StatusCode}, ignoring", url, response.StatusCode);
 653                        continue;
 654                    }
 655
 656                    if (!response.IsSuccessStatusCode)
 657                    {
 658                        _logger.LogWarning("{Url} returned {StatusCode}, skipping all remaining requests", url, response
 659                        break;
 660                    }
 661
 662                    // If there's already an image of the same file size, skip it unless doing a full refresh
 663                    if (response.Content.Headers.ContentLength.HasValue && !refreshOptions.IsReplacingImage(imageType))
 664                    {
 665                        try
 666                        {
 667                            if (item.GetImages(imageType).Any(i => _fileSystem.GetFileInfo(i.Path).Length == response.Co
 668                            {
 669                                response.Content.Dispose();
 670                                continue;
 671                            }
 672                        }
 673                        catch (IOException ex)
 674                        {
 675                            _logger.LogError(ex, "Error examining images");
 676                        }
 677                    }
 678
 679                    var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
 680                    await using (stream.ConfigureAwait(false))
 681                    {
 682                        await _providerManager.SaveImage(
 683                            item,
 684                            stream,
 685                            response.Content.Headers.ContentType?.MediaType,
 686                            imageType,
 687                            null,
 688                            cancellationToken).ConfigureAwait(false);
 689                    }
 690
 691                    result.UpdateType |= ItemUpdateType.ImageUpdate;
 692                }
 693                catch (HttpRequestException)
 694                {
 695                    break;
 696                }
 697            }
 698        }
 699    }
 700}

Methods/Properties

.cctor()
.ctor(Microsoft.Extensions.Logging.ILogger,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Model.IO.IFileSystem)
RemoveImages(MediaBrowser.Controller.Entities.BaseItem,System.Boolean)
ValidateImages(MediaBrowser.Controller.Entities.BaseItem,System.Collections.Generic.IEnumerable`1<MediaBrowser.Controller.Providers.IImageProvider>,MediaBrowser.Controller.Providers.ImageRefreshOptions)
ContainsImages(MediaBrowser.Controller.Entities.BaseItem,System.Collections.Generic.List`1<MediaBrowser.Model.Entities.ImageType>,MediaBrowser.Model.Configuration.TypeOptions,System.Int32)
PruneImages(MediaBrowser.Controller.Entities.BaseItem,System.Collections.Generic.IReadOnlyList`1<MediaBrowser.Controller.Entities.ItemImageInfo>)
UpdateReplaceImages(MediaBrowser.Controller.Providers.ImageRefreshOptions,System.Collections.Generic.ICollection`1<MediaBrowser.Model.Entities.ImageType>)
MergeImages(MediaBrowser.Controller.Entities.BaseItem,System.Collections.Generic.IReadOnlyList`1<MediaBrowser.Controller.Providers.LocalImageInfo>,MediaBrowser.Controller.Providers.ImageRefreshOptions)
GetFirstLocalImageInfoByType(System.Collections.Generic.IReadOnlyList`1<MediaBrowser.Controller.Providers.LocalImageInfo>,MediaBrowser.Model.Entities.ImageType)
UpdateMultiImages(MediaBrowser.Controller.Entities.BaseItem,System.Collections.Generic.IReadOnlyList`1<MediaBrowser.Controller.Providers.LocalImageInfo>,MediaBrowser.Model.Entities.ImageType)
EnableImageStub(MediaBrowser.Controller.Entities.BaseItem)
SaveImageStub(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Model.Entities.ImageType,System.Collections.Generic.IEnumerable`1<System.String>)
SaveImageStub(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Model.Entities.ImageType,System.Collections.Generic.IEnumerable`1<System.String>,System.Int32)