< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Manager.ImageSaver
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Manager/ImageSaver.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 156
Coverable lines: 156
Total lines: 701
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 132
Branch coverage: 0%
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
.ctor(...)100%210%
get_EnableExtraThumbsDuplication()100%210%
SaveImage(...)100%210%
SetHidden(...)100%210%
GetSavePaths(...)0%2040%
GetCurrentImage(...)100%210%
SetImagePath(...)100%210%
GetStandardSavePath(...)0%7140840%
GetBackdropSaveFilename(...)0%4260%
GetCompatibleSavePaths(...)0%1332360%
GetSavePathForItemInMixedFolder(...)0%620%

File(s)

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

#LineLine coverage
 1#nullable disable
 2
 3#pragma warning disable CS1591
 4
 5using System;
 6using System.Collections.Generic;
 7using System.Globalization;
 8using System.IO;
 9using System.Linq;
 10using System.Threading;
 11using System.Threading.Tasks;
 12using Jellyfin.Extensions;
 13using MediaBrowser.Common.Configuration;
 14using MediaBrowser.Controller.Configuration;
 15using MediaBrowser.Controller.Entities;
 16using MediaBrowser.Controller.Entities.Audio;
 17using MediaBrowser.Controller.IO;
 18using MediaBrowser.Controller.Library;
 19using MediaBrowser.Model.Configuration;
 20using MediaBrowser.Model.Entities;
 21using MediaBrowser.Model.IO;
 22using MediaBrowser.Model.Net;
 23using Microsoft.Extensions.Logging;
 24using Episode = MediaBrowser.Controller.Entities.TV.Episode;
 25using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
 26using Person = MediaBrowser.Controller.Entities.Person;
 27using Season = MediaBrowser.Controller.Entities.TV.Season;
 28
 29namespace MediaBrowser.Providers.Manager
 30{
 31    /// <summary>
 32    /// Class ImageSaver.
 33    /// </summary>
 34    public class ImageSaver
 35    {
 36        /// <summary>
 37        /// The _config.
 38        /// </summary>
 39        private readonly IServerConfigurationManager _config;
 40
 41        /// <summary>
 42        /// The _directory watchers.
 43        /// </summary>
 44        private readonly ILibraryMonitor _libraryMonitor;
 45        private readonly IFileSystem _fileSystem;
 46        private readonly ILogger _logger;
 47
 48        /// <summary>
 49        /// Initializes a new instance of the <see cref="ImageSaver" /> class.
 50        /// </summary>
 51        /// <param name="config">The config.</param>
 52        /// <param name="libraryMonitor">The directory watchers.</param>
 53        /// <param name="fileSystem">The file system.</param>
 54        /// <param name="logger">The logger.</param>
 55        public ImageSaver(IServerConfigurationManager config, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, IL
 56        {
 057            _config = config;
 058            _libraryMonitor = libraryMonitor;
 059            _fileSystem = fileSystem;
 060            _logger = logger;
 061        }
 62
 63        private bool EnableExtraThumbsDuplication
 64        {
 65            get
 66            {
 067                var config = _config.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
 68
 069                return config.EnableExtraThumbsDuplication;
 70            }
 71        }
 72
 73        /// <summary>
 74        /// Saves the image.
 75        /// </summary>
 76        /// <param name="item">The item.</param>
 77        /// <param name="source">The source.</param>
 78        /// <param name="mimeType">Type of the MIME.</param>
 79        /// <param name="type">The type.</param>
 80        /// <param name="imageIndex">Index of the image.</param>
 81        /// <param name="cancellationToken">The cancellation token.</param>
 82        /// <returns>Task.</returns>
 83        /// <exception cref="ArgumentNullException">mimeType.</exception>
 84        public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, Cancellati
 85        {
 086            return SaveImage(item, source, mimeType, type, imageIndex, null, cancellationToken);
 87        }
 88
 89        public async Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, bool
 90        {
 91            ArgumentException.ThrowIfNullOrEmpty(mimeType);
 92
 93            var saveLocally = item.SupportsLocalMetadata && item.IsSaveLocalMetadataEnabled() && !item.ExtraType.HasValu
 94
 95            if (type != ImageType.Primary && item is Episode)
 96            {
 97                saveLocally = false;
 98            }
 99
 100            if (!item.IsFileProtocol)
 101            {
 102                saveLocally = false;
 103
 104                // If season is virtual under a physical series, save locally
 105                if (item is Season season)
 106                {
 107                    var series = season.Series;
 108
 109                    if (series is not null && series.SupportsLocalMetadata && series.IsSaveLocalMetadataEnabled())
 110                    {
 111                        saveLocally = true;
 112                    }
 113                }
 114            }
 115
 116            if (saveLocallyWithMedia.HasValue && !saveLocallyWithMedia.Value)
 117            {
 118                saveLocally = saveLocallyWithMedia.Value;
 119            }
 120
 121            if (!imageIndex.HasValue && item.AllowsMultipleImages(type))
 122            {
 123                imageIndex = item.GetImages(type).Count();
 124            }
 125
 126            var index = imageIndex ?? 0;
 127
 128            var paths = GetSavePaths(item, type, imageIndex, mimeType, saveLocally);
 129
 130            string[] retryPaths = [];
 131            if (saveLocally)
 132            {
 133                retryPaths = GetSavePaths(item, type, imageIndex, mimeType, false);
 134            }
 135
 136            // If there are more than one output paths, the stream will need to be seekable
 137            if (paths.Length > 1 && !source.CanSeek)
 138            {
 139                var memoryStream = new MemoryStream();
 140                await using (source.ConfigureAwait(false))
 141                {
 142                    await source.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
 143                }
 144
 145                source = memoryStream;
 146            }
 147
 148            var currentImage = GetCurrentImage(item, type, index);
 149            var currentImageIsLocalFile = currentImage is not null && currentImage.IsLocalFile;
 150            var currentImagePath = currentImage?.Path;
 151
 152            var savedPaths = new List<string>();
 153
 154            await using (source.ConfigureAwait(false))
 155            {
 156                for (int i = 0; i < paths.Length; i++)
 157                {
 158                    if (i != 0)
 159                    {
 160                        source.Position = 0;
 161                    }
 162
 163                    string retryPath = null;
 164                    if (paths.Length == retryPaths.Length)
 165                    {
 166                        retryPath = retryPaths[i];
 167                    }
 168
 169                    var savedPath = await SaveImageToLocation(source, paths[i], retryPath, cancellationToken).ConfigureA
 170                    savedPaths.Add(savedPath);
 171                }
 172            }
 173
 174            // Set the path into the item
 175            SetImagePath(item, type, imageIndex, savedPaths[0]);
 176
 177            // Delete the current path
 178            if (currentImageIsLocalFile
 179                && !savedPaths.Contains(currentImagePath, StringComparison.OrdinalIgnoreCase)
 180                && (saveLocally || currentImagePath.Contains(_config.ApplicationPaths.InternalMetadataPath, StringCompar
 181            {
 182                var currentPath = currentImagePath;
 183
 184                _logger.LogInformation("Deleting previous image {0}", currentPath);
 185
 186                _libraryMonitor.ReportFileSystemChangeBeginning(currentPath);
 187
 188                try
 189                {
 190                    _fileSystem.DeleteFile(currentPath);
 191
 192                    // Remove local episode metadata directory if it exists and is empty
 193                    var directory = Path.GetDirectoryName(currentPath);
 194                    if (item is Episode && directory.Equals("metadata", StringComparison.Ordinal))
 195                    {
 196                        var parentDirectoryPath = Directory.GetParent(currentPath).FullName;
 197                        if (_fileSystem.DirectoryExists(parentDirectoryPath) && !_fileSystem.GetFiles(parentDirectoryPat
 198                        {
 199                            try
 200                            {
 201                                _logger.LogInformation("Deleting empty local metadata folder {Folder}", parentDirectoryP
 202                                Directory.Delete(parentDirectoryPath);
 203                            }
 204                            catch (UnauthorizedAccessException ex)
 205                            {
 206                                _logger.LogError(ex, "Error deleting directory {Path}", parentDirectoryPath);
 207                            }
 208                            catch (IOException ex)
 209                            {
 210                                _logger.LogError(ex, "Error deleting directory {Path}", parentDirectoryPath);
 211                            }
 212                        }
 213                    }
 214                }
 215                catch (FileNotFoundException)
 216                {
 217                }
 218                finally
 219                {
 220                    _libraryMonitor.ReportFileSystemChangeComplete(currentPath, false);
 221                }
 222            }
 223        }
 224
 225        public async Task SaveImage(Stream source, string path)
 226        {
 227            await SaveImageToLocation(source, path, path, CancellationToken.None).ConfigureAwait(false);
 228        }
 229
 230        private async Task<string> SaveImageToLocation(Stream source, string path, string retryPath, CancellationToken c
 231        {
 232            try
 233            {
 234                await SaveImageToLocation(source, path, cancellationToken).ConfigureAwait(false);
 235                return path;
 236            }
 237            catch (UnauthorizedAccessException)
 238            {
 239                var retry = !string.IsNullOrWhiteSpace(retryPath) &&
 240                    !string.Equals(path, retryPath, StringComparison.OrdinalIgnoreCase);
 241
 242                if (retry)
 243                {
 244                    _logger.LogError("UnauthorizedAccessException - Access to path {0} is denied. Will retry saving to {
 245                }
 246                else
 247                {
 248                    throw;
 249                }
 250            }
 251            catch (IOException ex)
 252            {
 253                var retry = !string.IsNullOrWhiteSpace(retryPath) &&
 254                    !string.Equals(path, retryPath, StringComparison.OrdinalIgnoreCase);
 255
 256                if (retry)
 257                {
 258                    _logger.LogError(ex, "IOException saving to {0}. Will retry saving to {1}", path, retryPath);
 259                }
 260                else
 261                {
 262                    throw;
 263                }
 264            }
 265
 266            await SaveImageToLocation(source, retryPath, cancellationToken).ConfigureAwait(false);
 267            return retryPath;
 268        }
 269
 270        /// <summary>
 271        /// Saves the image to location.
 272        /// </summary>
 273        /// <param name="source">The source.</param>
 274        /// <param name="path">The path.</param>
 275        /// <param name="cancellationToken">The cancellation token.</param>
 276        /// <returns>Task.</returns>
 277        private async Task SaveImageToLocation(Stream source, string path, CancellationToken cancellationToken)
 278        {
 279            _logger.LogDebug("Saving image to {0}", path);
 280
 281            var parentFolder = Path.GetDirectoryName(path);
 282
 283            try
 284            {
 285                _libraryMonitor.ReportFileSystemChangeBeginning(path);
 286                _libraryMonitor.ReportFileSystemChangeBeginning(parentFolder);
 287
 288                Directory.CreateDirectory(Path.GetDirectoryName(path));
 289
 290                _fileSystem.SetAttributes(path, false, false);
 291
 292                var fileStreamOptions = AsyncFile.WriteOptions;
 293                fileStreamOptions.Mode = FileMode.Create;
 294                if (source.CanSeek)
 295                {
 296                    fileStreamOptions.PreallocationSize = source.Length;
 297                }
 298
 299                var fs = new FileStream(path, fileStreamOptions);
 300                await using (fs.ConfigureAwait(false))
 301                {
 302                    await source.CopyToAsync(fs, cancellationToken).ConfigureAwait(false);
 303                }
 304
 305                if (_config.Configuration.SaveMetadataHidden)
 306                {
 307                    SetHidden(path, true);
 308                }
 309            }
 310            finally
 311            {
 312                _libraryMonitor.ReportFileSystemChangeComplete(path, false);
 313                _libraryMonitor.ReportFileSystemChangeComplete(parentFolder, false);
 314            }
 315        }
 316
 317        private void SetHidden(string path, bool hidden)
 318        {
 319            try
 320            {
 0321                _fileSystem.SetHidden(path, hidden);
 0322            }
 0323            catch (Exception ex)
 324            {
 0325                _logger.LogError(ex, "Error setting hidden attribute on {0}", path);
 0326            }
 0327        }
 328
 329        /// <summary>
 330        /// Gets the save paths.
 331        /// </summary>
 332        /// <param name="item">The item.</param>
 333        /// <param name="type">The type.</param>
 334        /// <param name="imageIndex">Index of the image.</param>
 335        /// <param name="mimeType">Type of the MIME.</param>
 336        /// <param name="saveLocally">if set to <c>true</c> [save locally].</param>
 337        /// <returns>IEnumerable{System.String}.</returns>
 338        private string[] GetSavePaths(BaseItem item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
 339        {
 0340            if (!saveLocally || (_config.Configuration.ImageSavingConvention == ImageSavingConvention.Legacy))
 341            {
 0342                return new[] { GetStandardSavePath(item, type, imageIndex, mimeType, saveLocally) };
 343            }
 344
 0345            return GetCompatibleSavePaths(item, type, imageIndex, mimeType);
 346        }
 347
 348        /// <summary>
 349        /// Gets the current image path.
 350        /// </summary>
 351        /// <param name="item">The item.</param>
 352        /// <param name="type">The type.</param>
 353        /// <param name="imageIndex">Index of the image.</param>
 354        /// <returns>System.String.</returns>
 355        /// <exception cref="ArgumentNullException">
 356        /// imageIndex
 357        /// or
 358        /// imageIndex.
 359        /// </exception>
 360        private ItemImageInfo GetCurrentImage(BaseItem item, ImageType type, int imageIndex)
 361        {
 0362            return item.GetImageInfo(type, imageIndex);
 363        }
 364
 365        /// <summary>
 366        /// Sets the image path.
 367        /// </summary>
 368        /// <param name="item">The item.</param>
 369        /// <param name="type">The type.</param>
 370        /// <param name="imageIndex">Index of the image.</param>
 371        /// <param name="path">The path.</param>
 372        /// <exception cref="ArgumentNullException">imageIndex
 373        /// or
 374        /// imageIndex.
 375        /// </exception>
 376        private void SetImagePath(BaseItem item, ImageType type, int? imageIndex, string path)
 377        {
 0378            item.SetImagePath(type, imageIndex ?? 0, _fileSystem.GetFileInfo(path));
 0379        }
 380
 381        /// <summary>
 382        /// Gets the save path.
 383        /// </summary>
 384        /// <param name="item">The item.</param>
 385        /// <param name="type">The type.</param>
 386        /// <param name="imageIndex">Index of the image.</param>
 387        /// <param name="mimeType">Type of the MIME.</param>
 388        /// <param name="saveLocally">if set to <c>true</c> [save locally].</param>
 389        /// <returns>System.String.</returns>
 390        /// <exception cref="ArgumentNullException">
 391        /// imageIndex
 392        /// or
 393        /// imageIndex.
 394        /// </exception>
 395        private string GetStandardSavePath(BaseItem item, ImageType type, int? imageIndex, string mimeType, bool saveLoc
 396        {
 0397            var season = item as Season;
 0398            var extension = MimeTypes.ToExtension(mimeType);
 399
 0400            if (string.IsNullOrWhiteSpace(extension))
 401            {
 0402                throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Unable to determine image file 
 403            }
 404
 0405            if (string.Equals(extension, ".jpeg", StringComparison.OrdinalIgnoreCase))
 406            {
 0407                extension = ".jpg";
 408            }
 409
 0410            extension = extension.ToLowerInvariant();
 411
 0412            if (type == ImageType.Primary && saveLocally)
 413            {
 0414                if (season is not null && season.IndexNumber.HasValue)
 415                {
 0416                    var seriesFolder = season.SeriesPath;
 417
 0418                    var seasonMarker = season.IndexNumber.Value == 0
 0419                                           ? "-specials"
 0420                                           : season.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
 421
 0422                    var imageFilename = "season" + seasonMarker + "-poster" + extension;
 423
 0424                    return Path.Combine(seriesFolder, imageFilename);
 425                }
 426            }
 427
 0428            if (type == ImageType.Backdrop && saveLocally)
 429            {
 0430                if (season is not null
 0431                    && season.IndexNumber.HasValue
 0432                    && (imageIndex is null || imageIndex == 0))
 433                {
 0434                    var seriesFolder = season.SeriesPath;
 435
 0436                    var seasonMarker = season.IndexNumber.Value == 0
 0437                                        ? "-specials"
 0438                                        : season.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
 439
 0440                    var imageFilename = "season" + seasonMarker + "-fanart" + extension;
 441
 0442                    return Path.Combine(seriesFolder, imageFilename);
 443                }
 444            }
 445
 0446            if (type == ImageType.Thumb && saveLocally)
 447            {
 0448                if (season is not null && season.IndexNumber.HasValue)
 449                {
 0450                    var seriesFolder = season.SeriesPath;
 451
 0452                    var seasonMarker = season.IndexNumber.Value == 0
 0453                                           ? "-specials"
 0454                                           : season.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
 455
 0456                    var imageFilename = "season" + seasonMarker + "-landscape" + extension;
 457
 0458                    return Path.Combine(seriesFolder, imageFilename);
 459                }
 460
 0461                if (item.IsInMixedFolder)
 462                {
 0463                    return GetSavePathForItemInMixedFolder(item, type, "landscape", extension);
 464                }
 465
 0466                return Path.Combine(item.ContainingFolderPath, "landscape" + extension);
 467            }
 468
 0469            if (type == ImageType.Banner && saveLocally)
 470            {
 0471                if (season is not null && season.IndexNumber.HasValue)
 472                {
 0473                    var seriesFolder = season.SeriesPath;
 474
 0475                    var seasonMarker = season.IndexNumber.Value == 0
 0476                                           ? "-specials"
 0477                                           : season.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
 478
 0479                    var imageFilename = "season" + seasonMarker + "-banner" + extension;
 480
 0481                    return Path.Combine(seriesFolder, imageFilename);
 482                }
 483            }
 484
 485            string filename;
 0486            var folderName = item is MusicAlbum ||
 0487                item is MusicArtist ||
 0488                item is PhotoAlbum ||
 0489                item is Person ||
 0490                (saveLocally && _config.Configuration.ImageSavingConvention == ImageSavingConvention.Legacy) ?
 0491                "folder" :
 0492                "poster";
 493
 494            switch (type)
 495            {
 496                case ImageType.Art:
 0497                    filename = "clearart";
 0498                    break;
 499                case ImageType.BoxRear:
 0500                    filename = "back";
 0501                    break;
 502                case ImageType.Thumb:
 0503                    filename = "landscape";
 0504                    break;
 505                case ImageType.Disc:
 0506                    filename = item is MusicAlbum ? "cdart" : "disc";
 0507                    break;
 508                case ImageType.Primary:
 0509                    filename = saveLocally && item is Episode ? Path.GetFileNameWithoutExtension(item.Path) : folderName
 0510                    break;
 511                case ImageType.Backdrop:
 0512                    filename = GetBackdropSaveFilename(item.GetImages(type), "backdrop", "backdrop", imageIndex);
 0513                    break;
 514                default:
 0515                    filename = type.ToString().ToLowerInvariant();
 516                    break;
 517            }
 518
 0519            string path = null;
 0520            if (saveLocally)
 521            {
 0522                if (type == ImageType.Primary && item is Episode)
 523                {
 0524                    path = Path.Combine(Path.GetDirectoryName(item.Path), filename + "-thumb" + extension);
 525                }
 0526                else if (item.IsInMixedFolder)
 527                {
 0528                    path = GetSavePathForItemInMixedFolder(item, type, filename, extension);
 529                }
 530
 0531                if (string.IsNullOrEmpty(path))
 532                {
 0533                    path = Path.Combine(item.ContainingFolderPath, filename + extension);
 534                }
 535            }
 536
 537            // None of the save local conditions passed, so store it in our internal folders
 0538            if (string.IsNullOrEmpty(path))
 539            {
 0540                if (string.IsNullOrEmpty(filename))
 541                {
 0542                    filename = folderName;
 543                }
 544
 0545                path = Path.Combine(item.GetInternalMetadataPath(), filename + extension);
 546            }
 547
 0548            return path;
 549        }
 550
 551        private string GetBackdropSaveFilename(IEnumerable<ItemImageInfo> images, string zeroIndexFilename, string numbe
 552        {
 0553            if (index.HasValue && index.Value == 0)
 554            {
 0555                return zeroIndexFilename;
 556            }
 557
 0558            var filenames = images.Select(i => Path.GetFileNameWithoutExtension(i.Path)).ToList();
 559
 0560            var current = 1;
 0561            while (filenames.Contains(numberedIndexPrefix + current.ToString(CultureInfo.InvariantCulture), StringCompar
 562            {
 0563                current++;
 564            }
 565
 0566            return numberedIndexPrefix + current.ToString(CultureInfo.InvariantCulture);
 567        }
 568
 569        /// <summary>
 570        /// Gets the compatible save paths.
 571        /// </summary>
 572        /// <param name="item">The item.</param>
 573        /// <param name="type">The type.</param>
 574        /// <param name="imageIndex">Index of the image.</param>
 575        /// <param name="mimeType">Type of the MIME.</param>
 576        /// <returns>IEnumerable{System.String}.</returns>
 577        /// <exception cref="ArgumentNullException">imageIndex.</exception>
 578        private string[] GetCompatibleSavePaths(BaseItem item, ImageType type, int? imageIndex, string mimeType)
 579        {
 0580            var season = item as Season;
 581
 0582            var extension = MimeTypes.ToExtension(mimeType);
 583
 584            // Backdrop paths
 0585            if (type == ImageType.Backdrop)
 586            {
 0587                if (!imageIndex.HasValue)
 588                {
 0589                    throw new ArgumentNullException(nameof(imageIndex));
 590                }
 591
 0592                if (imageIndex.Value == 0)
 593                {
 0594                    if (item.IsInMixedFolder)
 595                    {
 0596                        return new[] { GetSavePathForItemInMixedFolder(item, type, "fanart", extension) };
 597                    }
 598
 0599                    if (season is not null && season.IndexNumber.HasValue)
 600                    {
 0601                        var seriesFolder = season.SeriesPath;
 602
 0603                        var seasonMarker = season.IndexNumber.Value == 0
 0604                                               ? "-specials"
 0605                                               : season.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
 606
 0607                        var imageFilename = "season" + seasonMarker + "-fanart" + extension;
 608
 0609                        return new[] { Path.Combine(seriesFolder, imageFilename) };
 610                    }
 611
 0612                    return new[]
 0613                        {
 0614                            Path.Combine(item.ContainingFolderPath, "fanart" + extension)
 0615                        };
 616                }
 617
 0618                var outputIndex = imageIndex.Value;
 619
 0620                if (item.IsInMixedFolder)
 621                {
 0622                    return new[] { GetSavePathForItemInMixedFolder(item, type, "fanart" + outputIndex.ToString(CultureIn
 623                }
 624
 0625                var extraFanartFilename = GetBackdropSaveFilename(item.GetImages(ImageType.Backdrop), "fanart", "fanart"
 626
 0627                var list = new List<string>
 0628                {
 0629                    Path.Combine(item.ContainingFolderPath, "extrafanart", extraFanartFilename + extension)
 0630                };
 631
 0632                if (EnableExtraThumbsDuplication)
 633                {
 0634                    list.Add(Path.Combine(item.ContainingFolderPath, "extrathumbs", "thumb" + outputIndex.ToString(Cultu
 635                }
 636
 0637                return list.ToArray();
 638            }
 639
 0640            if (type == ImageType.Primary)
 641            {
 0642                if (season is not null && season.IndexNumber.HasValue)
 643                {
 0644                    var seriesFolder = season.SeriesPath;
 645
 0646                    var seasonMarker = season.IndexNumber.Value == 0
 0647                                           ? "-specials"
 0648                                           : season.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
 649
 0650                    var imageFilename = "season" + seasonMarker + "-poster" + extension;
 651
 0652                    return new[] { Path.Combine(seriesFolder, imageFilename) };
 653                }
 654
 0655                if (item is Episode)
 656                {
 0657                    var seasonFolder = Path.GetDirectoryName(item.Path);
 658
 0659                    var imageFilename = Path.GetFileNameWithoutExtension(item.Path) + "-thumb" + extension;
 660
 0661                    return new[] { Path.Combine(seasonFolder, imageFilename) };
 662                }
 663
 0664                if (item.IsInMixedFolder || item is MusicVideo)
 665                {
 0666                    return new[] { GetSavePathForItemInMixedFolder(item, type, string.Empty, extension) };
 667                }
 668
 0669                if (item is MusicAlbum || item is MusicArtist)
 670                {
 0671                    return new[] { Path.Combine(item.ContainingFolderPath, "folder" + extension) };
 672                }
 673
 0674                return new[] { Path.Combine(item.ContainingFolderPath, "poster" + extension) };
 675            }
 676
 677            // All other paths are the same
 0678            return new[] { GetStandardSavePath(item, type, imageIndex, mimeType, true) };
 679        }
 680
 681        /// <summary>
 682        /// Gets the save path for item in mixed folder.
 683        /// </summary>
 684        /// <param name="item">The item.</param>
 685        /// <param name="type">The type.</param>
 686        /// <param name="imageFilename">The image filename.</param>
 687        /// <param name="extension">The extension.</param>
 688        /// <returns>System.String.</returns>
 689        private string GetSavePathForItemInMixedFolder(BaseItem item, ImageType type, string imageFilename, string exten
 690        {
 0691            if (type == ImageType.Primary)
 692            {
 0693                imageFilename = "poster";
 694            }
 695
 0696            var folder = Path.GetDirectoryName(item.Path);
 697
 0698            return Path.Combine(folder, Path.GetFileNameWithoutExtension(item.Path) + "-" + imageFilename + extension);
 699        }
 700    }
 701}

Methods/Properties

.ctor(MediaBrowser.Controller.Configuration.IServerConfigurationManager,MediaBrowser.Controller.Library.ILibraryMonitor,MediaBrowser.Model.IO.IFileSystem,Microsoft.Extensions.Logging.ILogger)
get_EnableExtraThumbsDuplication()
SaveImage(MediaBrowser.Controller.Entities.BaseItem,System.IO.Stream,System.String,MediaBrowser.Model.Entities.ImageType,System.Nullable`1<System.Int32>,System.Threading.CancellationToken)
SetHidden(System.String,System.Boolean)
GetSavePaths(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Model.Entities.ImageType,System.Nullable`1<System.Int32>,System.String,System.Boolean)
GetCurrentImage(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Model.Entities.ImageType,System.Int32)
SetImagePath(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Model.Entities.ImageType,System.Nullable`1<System.Int32>,System.String)
GetStandardSavePath(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Model.Entities.ImageType,System.Nullable`1<System.Int32>,System.String,System.Boolean)
GetBackdropSaveFilename(System.Collections.Generic.IEnumerable`1<MediaBrowser.Controller.Entities.ItemImageInfo>,System.String,System.String,System.Nullable`1<System.Int32>)
GetCompatibleSavePaths(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Model.Entities.ImageType,System.Nullable`1<System.Int32>,System.String)
GetSavePathForItemInMixedFolder(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Model.Entities.ImageType,System.String,System.String)