< Summary - Jellyfin

Information
Class: MediaBrowser.LocalMetadata.Images.LocalImageProvider
Assembly: MediaBrowser.LocalMetadata
File(s): /srv/git/jellyfin/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
Line coverage
69%
Covered lines: 143
Uncovered lines: 64
Coverable lines: 207
Total lines: 494
Line coverage: 69%
Branch coverage
66%
Covered branches: 100
Total branches: 150
Branch coverage: 66.6%
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%
get_Name()100%11100%
get_Order()100%11100%
Supports(...)25%103.811630%
GetFiles(...)50%4.13480%
GetImages(...)100%11100%
GetImages(...)100%210%
GetImages(...)100%11100%
PopulateImages(...)84%92.545074.28%
PopulatePrimaryImages(...)64.28%57.052866.66%
PopulateBackdrops(...)87.5%8.02893.33%
PopulateBackdropsFromExtraFanart(...)100%210%
PopulateBackdrops(...)100%1414100%
PopulateSeasonImagesFromSeriesFolder(...)0%110100%
AddImage(...)75%4.13480%
AddImage(...)50%3.19233.33%
GetImage(...)64.28%38.51450%

File(s)

/srv/git/jellyfin/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Globalization;
 4using System.IO;
 5using System.Linq;
 6using Jellyfin.Extensions;
 7using MediaBrowser.Controller.Entities;
 8using MediaBrowser.Controller.Entities.Audio;
 9using MediaBrowser.Controller.Entities.Movies;
 10using MediaBrowser.Controller.Entities.TV;
 11using MediaBrowser.Controller.Providers;
 12using MediaBrowser.Model.Entities;
 13using MediaBrowser.Model.IO;
 14
 15namespace MediaBrowser.LocalMetadata.Images
 16{
 17    /// <summary>
 18    /// Local image provider.
 19    /// </summary>
 20    public class LocalImageProvider : ILocalImageProvider, IHasOrder
 21    {
 122        private static readonly string[] _commonImageFileNames =
 123        {
 124            "poster",
 125            "folder",
 126            "cover",
 127            "default"
 128        };
 29
 130        private static readonly string[] _musicImageFileNames =
 131        {
 132            "folder",
 133            "poster",
 134            "cover",
 135            "jacket",
 136            "default"
 137        };
 38
 139        private static readonly string[] _personImageFileNames =
 140        {
 141            "folder",
 142            "poster"
 143        };
 44
 145        private static readonly string[] _seriesImageFileNames =
 146        {
 147            "poster",
 148            "folder",
 149            "cover",
 150            "default",
 151            "show"
 152        };
 53
 154        private static readonly string[] _videoImageFileNames =
 155        {
 156            "poster",
 157            "folder",
 158            "cover",
 159            "default",
 160            "movie"
 161        };
 62
 63        private readonly IFileSystem _fileSystem;
 64
 65        /// <summary>
 66        /// Initializes a new instance of the <see cref="LocalImageProvider"/> class.
 67        /// </summary>
 68        /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
 69        public LocalImageProvider(IFileSystem fileSystem)
 70        {
 2671            _fileSystem = fileSystem;
 2672        }
 73
 74        /// <inheritdoc />
 4675        public string Name => "Local Images";
 76
 77        /// <inheritdoc />
 4678        public int Order => 0;
 79
 80        /// <inheritdoc />
 81        public bool Supports(BaseItem item)
 82        {
 4683            if (item.SupportsLocalMetadata)
 84            {
 85                // Episode has its own provider
 4686                if (item is Episode || item is Audio || item is Photo)
 87                {
 088                    return false;
 89                }
 90
 4691                return true;
 92            }
 93
 094            if (item.LocationType == LocationType.Virtual)
 95            {
 096                var season = item as Season;
 097                var series = season?.Series;
 098                if (series is not null && series.IsFileProtocol)
 99                {
 0100                    return true;
 101                }
 102            }
 103
 0104            return false;
 105        }
 106
 107        private static IEnumerable<FileSystemMetadata> GetFiles(BaseItem item, bool includeDirectories, IDirectoryServic
 108        {
 46109            if (!item.IsFileProtocol)
 110            {
 0111                return Enumerable.Empty<FileSystemMetadata>();
 112            }
 113
 46114            var path = item.ContainingFolderPath;
 115
 116            // Exit if the cache dir does not exist, alternative solution is to create it, but that's a lot of empty dir
 46117            if (!Directory.Exists(path))
 118            {
 0119                return Enumerable.Empty<FileSystemMetadata>();
 120            }
 121
 46122            return directoryService.GetFileSystemEntries(path)
 46123                .Where(i =>
 46124                    (includeDirectories && i.IsDirectory)
 46125                    || BaseItem.SupportedImageExtensions.Contains(i.Extension, StringComparison.OrdinalIgnoreCase))
 46126                .OrderBy(i => Array.IndexOf(BaseItem.SupportedImageExtensions, i.Extension ?? string.Empty));
 127        }
 128
 129        /// <inheritdoc />
 130        public IEnumerable<LocalImageInfo> GetImages(BaseItem item, IDirectoryService directoryService)
 131        {
 46132            var files = GetFiles(item, true, directoryService).ToList();
 133
 46134            var list = new List<LocalImageInfo>();
 135
 46136            PopulateImages(item, list, files, true, directoryService);
 137
 46138            return list;
 139        }
 140
 141        /// <summary>
 142        /// Get images for item.
 143        /// </summary>
 144        /// <param name="item">The item.</param>
 145        /// <param name="path">The images path.</param>
 146        /// <param name="directoryService">Instance of the <see cref="IDirectoryService"/> interface.</param>
 147        /// <returns>The local image info.</returns>
 148        public IEnumerable<LocalImageInfo> GetImages(BaseItem item, string path, IDirectoryService directoryService)
 149        {
 0150            return GetImages(item, new[] { path }, directoryService);
 151        }
 152
 153        /// <summary>
 154        /// Get images for item from multiple paths.
 155        /// </summary>
 156        /// <param name="item">The item.</param>
 157        /// <param name="paths">The image paths.</param>
 158        /// <param name="directoryService">Instance of the <see cref="IDirectoryService"/> interface.</param>
 159        /// <returns>The local image info.</returns>
 160        public IEnumerable<LocalImageInfo> GetImages(BaseItem item, IEnumerable<string> paths, IDirectoryService directo
 161        {
 4162            IEnumerable<FileSystemMetadata> files = paths.SelectMany(i => _fileSystem.GetFiles(i, BaseItem.SupportedImag
 163
 4164            files = files
 4165                .OrderBy(i => Array.IndexOf(BaseItem.SupportedImageExtensions, i.Extension ?? string.Empty));
 166
 4167            var list = new List<LocalImageInfo>();
 168
 4169            PopulateImages(item, list, files.ToList(), false, directoryService);
 170
 4171            return list;
 172        }
 173
 174        private void PopulateImages(BaseItem item, List<LocalImageInfo> images, List<FileSystemMetadata> files, bool sup
 175        {
 50176            if (supportParentSeriesFiles)
 177            {
 46178                if (item is Season season)
 179                {
 0180                    PopulateSeasonImagesFromSeriesFolder(season, images, directoryService);
 181                }
 182            }
 183
 50184            var imagePrefix = item.FileNameWithoutExtension + "-";
 50185            var isInMixedFolder = item.IsInMixedFolder;
 186
 50187            PopulatePrimaryImages(item, images, files, imagePrefix, isInMixedFolder);
 188
 50189            var added = false;
 50190            var isEpisode = item is Episode;
 50191            var isSong = item.GetType() == typeof(Audio);
 50192            var isPerson = item is Person;
 193
 194            // Logo
 50195            if (!isEpisode && !isSong && !isPerson)
 196            {
 50197                added = AddImage(files, images, "logo", imagePrefix, isInMixedFolder, ImageType.Logo);
 50198                if (!added)
 199                {
 50200                    AddImage(files, images, "clearlogo", imagePrefix, isInMixedFolder, ImageType.Logo);
 201                }
 202            }
 203
 204            // Art
 50205            if (!isEpisode && !isSong && !isPerson)
 206            {
 50207                AddImage(files, images, "clearart", imagePrefix, isInMixedFolder, ImageType.Art);
 208            }
 209
 210            // For music albums, prefer cdart before disc
 50211            if (item is MusicAlbum)
 212            {
 0213                added = AddImage(files, images, "cdart", imagePrefix, isInMixedFolder, ImageType.Disc);
 214
 0215                if (!added)
 216                {
 0217                    AddImage(files, images, "disc", imagePrefix, isInMixedFolder, ImageType.Disc);
 218                }
 219            }
 50220            else if (item is Video || item is BoxSet)
 221            {
 0222                added = AddImage(files, images, "disc", imagePrefix, isInMixedFolder, ImageType.Disc);
 223
 0224                if (!added)
 225                {
 0226                    added = AddImage(files, images, "cdart", imagePrefix, isInMixedFolder, ImageType.Disc);
 227                }
 228
 0229                if (!added)
 230                {
 0231                    AddImage(files, images, "discart", imagePrefix, isInMixedFolder, ImageType.Disc);
 232                }
 233            }
 234
 235            // Banner
 50236            if (!isEpisode && !isSong && !isPerson)
 237            {
 50238                AddImage(files, images, "banner", imagePrefix, isInMixedFolder, ImageType.Banner);
 239            }
 240
 241            // Thumb
 50242            if (!isEpisode && !isSong && !isPerson)
 243            {
 50244                added = AddImage(files, images, "landscape", imagePrefix, isInMixedFolder, ImageType.Thumb);
 50245                if (!added)
 246                {
 50247                    AddImage(files, images, "thumb", imagePrefix, isInMixedFolder, ImageType.Thumb);
 248                }
 249            }
 250
 50251            if (!isEpisode && !isSong && !isPerson)
 252            {
 50253                PopulateBackdrops(item, images, files, imagePrefix, isInMixedFolder);
 254            }
 50255        }
 256
 257        private void PopulatePrimaryImages(BaseItem item, List<LocalImageInfo> images, List<FileSystemMetadata> files, s
 258        {
 259            string[] imageFileNames;
 260
 50261            if (item is MusicAlbum || item is MusicArtist || item is PhotoAlbum)
 262            {
 263                // these prefer folder
 0264                imageFileNames = _musicImageFileNames;
 265            }
 50266            else if (item is Person)
 267            {
 268                // these prefer folder
 0269                imageFileNames = _personImageFileNames;
 270            }
 50271            else if (item is Series)
 272            {
 0273                imageFileNames = _seriesImageFileNames;
 274            }
 50275            else if (item is Video && item is not Episode)
 276            {
 0277                imageFileNames = _videoImageFileNames;
 278            }
 279            else
 280            {
 50281                imageFileNames = _commonImageFileNames;
 282            }
 283
 50284            var fileNameWithoutExtension = item.FileNameWithoutExtension;
 50285            if (!string.IsNullOrEmpty(fileNameWithoutExtension))
 286            {
 50287                if (AddImage(files, images, fileNameWithoutExtension, ImageType.Primary))
 288                {
 0289                    return;
 290                }
 291            }
 292
 500293            foreach (var name in imageFileNames)
 294            {
 200295                if (AddImage(files, images, name, ImageType.Primary, imagePrefix))
 296                {
 0297                    return;
 298                }
 299            }
 300
 50301            if (!isInMixedFolder)
 302            {
 500303                foreach (var name in imageFileNames)
 304                {
 200305                    if (AddImage(files, images, name, ImageType.Primary))
 306                    {
 0307                        return;
 308                    }
 309                }
 310            }
 50311        }
 312
 313        private void PopulateBackdrops(BaseItem item, List<LocalImageInfo> images, List<FileSystemMetadata> files, strin
 314        {
 50315            if (!string.IsNullOrEmpty(item.Path))
 316            {
 50317                var name = item.FileNameWithoutExtension;
 318
 50319                if (!string.IsNullOrEmpty(name))
 320                {
 50321                    AddImage(files, images, name + "-fanart", ImageType.Backdrop, imagePrefix);
 322
 323                    // Support without the prefix if it's in it's own folder
 50324                    if (!isInMixedFolder)
 325                    {
 50326                        AddImage(files, images, name + "-fanart", ImageType.Backdrop);
 327                    }
 328                }
 329            }
 330
 50331            PopulateBackdrops(images, files, imagePrefix, "fanart", "fanart-", isInMixedFolder, ImageType.Backdrop);
 50332            PopulateBackdrops(images, files, imagePrefix, "background", "background-", isInMixedFolder, ImageType.Backdr
 50333            PopulateBackdrops(images, files, imagePrefix, "art", "art-", isInMixedFolder, ImageType.Backdrop);
 334
 50335            var extraFanartFolder = files
 50336                .FirstOrDefault(i => string.Equals(i.Name, "extrafanart", StringComparison.OrdinalIgnoreCase));
 337
 50338            if (extraFanartFolder is not null)
 339            {
 0340                PopulateBackdropsFromExtraFanart(extraFanartFolder.FullName, images);
 341            }
 342
 50343            PopulateBackdrops(images, files, imagePrefix, "backdrop", "backdrop", isInMixedFolder, ImageType.Backdrop);
 50344        }
 345
 346        private void PopulateBackdropsFromExtraFanart(string path, List<LocalImageInfo> images)
 347        {
 0348            var imageFiles = _fileSystem.GetFiles(path, BaseItem.SupportedImageExtensions, false, false);
 349
 0350            images.AddRange(imageFiles.Where(i => i.Length > 0).Select(i => new LocalImageInfo
 0351            {
 0352                FileInfo = i,
 0353                Type = ImageType.Backdrop
 0354            }));
 0355        }
 356
 357        private void PopulateBackdrops(List<LocalImageInfo> images, List<FileSystemMetadata> files, string imagePrefix, 
 358        {
 200359            AddImage(files, images, imagePrefix + firstFileName, type);
 360
 200361            var unfound = 0;
 1200362            for (var i = 1; i <= 20; i++)
 363            {
 364                // Screenshot Image
 600365                var found = AddImage(files, images, imagePrefix + subsequentFileNamePrefix + i, type);
 366
 600367                if (!found)
 368                {
 600369                    unfound++;
 370
 600371                    if (unfound >= 3)
 372                    {
 373                        break;
 374                    }
 375                }
 376            }
 377
 378            // Support without the prefix
 200379            if (!isInMixedFolder)
 380            {
 200381                AddImage(files, images, firstFileName, type);
 382
 200383                unfound = 0;
 1200384                for (var i = 1; i <= 20; i++)
 385                {
 386                    // Screenshot Image
 600387                    var found = AddImage(files, images, subsequentFileNamePrefix + i, type);
 388
 600389                    if (!found)
 390                    {
 600391                        unfound++;
 392
 600393                        if (unfound >= 3)
 394                        {
 395                            break;
 396                        }
 397                    }
 398                }
 399            }
 200400        }
 401
 402        private void PopulateSeasonImagesFromSeriesFolder(Season season, List<LocalImageInfo> images, IDirectoryService 
 403        {
 0404            var seasonNumber = season.IndexNumber;
 405
 0406            var series = season.Series;
 0407            if (!seasonNumber.HasValue || !series.IsFileProtocol)
 408            {
 0409                return;
 410            }
 411
 0412            var seriesFiles = GetFiles(series, false, directoryService).ToList();
 413
 414            // Try using the season name
 0415            var prefix = season.Name.Replace(" ", string.Empty, StringComparison.Ordinal).ToLowerInvariant();
 416
 0417            var filenamePrefixes = new List<string> { prefix };
 418
 0419            var seasonMarker = seasonNumber.Value == 0
 0420                                   ? "-specials"
 0421                                   : seasonNumber.Value.ToString("00", CultureInfo.InvariantCulture);
 422
 423            // Get this one directly from the file system since we have to go up a level
 0424            if (!string.Equals(prefix, seasonMarker, StringComparison.OrdinalIgnoreCase))
 425            {
 0426                filenamePrefixes.Add("season" + seasonMarker);
 427            }
 428
 0429            foreach (var filename in filenamePrefixes)
 430            {
 0431                AddImage(seriesFiles, images, filename + "-poster", ImageType.Primary);
 0432                AddImage(seriesFiles, images, filename + "-fanart", ImageType.Backdrop);
 0433                AddImage(seriesFiles, images, filename + "-banner", ImageType.Banner);
 0434                AddImage(seriesFiles, images, filename + "-landscape", ImageType.Thumb);
 435            }
 0436        }
 437
 438        private bool AddImage(List<FileSystemMetadata> files, List<LocalImageInfo> images, string name, string imagePref
 439        {
 300440            var added = AddImage(files, images, name, type, imagePrefix);
 441
 300442            if (!isInMixedFolder)
 443            {
 300444                if (AddImage(files, images, name, type))
 445                {
 0446                    added = true;
 447                }
 448            }
 449
 300450            return added;
 451        }
 452
 453        private static bool AddImage(IReadOnlyList<FileSystemMetadata> files, List<LocalImageInfo> images, string name, 
 454        {
 2750455            var image = GetImage(files, name, prefix);
 456
 2750457            if (image is null)
 458            {
 2750459                return false;
 460            }
 461
 0462            images.Add(new LocalImageInfo
 0463            {
 0464                FileInfo = image,
 0465                Type = type
 0466            });
 467
 0468            return true;
 469        }
 470
 471        private static FileSystemMetadata? GetImage(IReadOnlyList<FileSystemMetadata> files, string name, string? prefix
 472        {
 2750473            var fileNameLength = name.Length + (prefix?.Length ?? 0);
 8030474            for (var i = 0; i < files.Count; i++)
 475            {
 1265476                var file = files[i];
 1265477                if (file.IsDirectory || file.Length <= 0)
 478                {
 479                    continue;
 480                }
 481
 0482                var fileName = Path.GetFileNameWithoutExtension(file.FullName.AsSpan());
 0483                if (fileName.Length == fileNameLength
 0484                    && fileName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)
 0485                    && fileName.EndsWith(name, StringComparison.OrdinalIgnoreCase))
 486                {
 0487                    return file;
 488                }
 489            }
 490
 2750491            return null;
 492        }
 493    }
 494}

Methods/Properties

.cctor()
.ctor(MediaBrowser.Model.IO.IFileSystem)
get_Name()
get_Order()
Supports(MediaBrowser.Controller.Entities.BaseItem)
GetFiles(MediaBrowser.Controller.Entities.BaseItem,System.Boolean,MediaBrowser.Controller.Providers.IDirectoryService)
GetImages(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Providers.IDirectoryService)
GetImages(MediaBrowser.Controller.Entities.BaseItem,System.String,MediaBrowser.Controller.Providers.IDirectoryService)
GetImages(MediaBrowser.Controller.Entities.BaseItem,System.Collections.Generic.IEnumerable`1<System.String>,MediaBrowser.Controller.Providers.IDirectoryService)
PopulateImages(MediaBrowser.Controller.Entities.BaseItem,System.Collections.Generic.List`1<MediaBrowser.Controller.Providers.LocalImageInfo>,System.Collections.Generic.List`1<MediaBrowser.Model.IO.FileSystemMetadata>,System.Boolean,MediaBrowser.Controller.Providers.IDirectoryService)
PopulatePrimaryImages(MediaBrowser.Controller.Entities.BaseItem,System.Collections.Generic.List`1<MediaBrowser.Controller.Providers.LocalImageInfo>,System.Collections.Generic.List`1<MediaBrowser.Model.IO.FileSystemMetadata>,System.String,System.Boolean)
PopulateBackdrops(MediaBrowser.Controller.Entities.BaseItem,System.Collections.Generic.List`1<MediaBrowser.Controller.Providers.LocalImageInfo>,System.Collections.Generic.List`1<MediaBrowser.Model.IO.FileSystemMetadata>,System.String,System.Boolean)
PopulateBackdropsFromExtraFanart(System.String,System.Collections.Generic.List`1<MediaBrowser.Controller.Providers.LocalImageInfo>)
PopulateBackdrops(System.Collections.Generic.List`1<MediaBrowser.Controller.Providers.LocalImageInfo>,System.Collections.Generic.List`1<MediaBrowser.Model.IO.FileSystemMetadata>,System.String,System.String,System.String,System.Boolean,MediaBrowser.Model.Entities.ImageType)
PopulateSeasonImagesFromSeriesFolder(MediaBrowser.Controller.Entities.TV.Season,System.Collections.Generic.List`1<MediaBrowser.Controller.Providers.LocalImageInfo>,MediaBrowser.Controller.Providers.IDirectoryService)
AddImage(System.Collections.Generic.List`1<MediaBrowser.Model.IO.FileSystemMetadata>,System.Collections.Generic.List`1<MediaBrowser.Controller.Providers.LocalImageInfo>,System.String,System.String,System.Boolean,MediaBrowser.Model.Entities.ImageType)
AddImage(System.Collections.Generic.IReadOnlyList`1<MediaBrowser.Model.IO.FileSystemMetadata>,System.Collections.Generic.List`1<MediaBrowser.Controller.Providers.LocalImageInfo>,System.String,MediaBrowser.Model.Entities.ImageType,System.String)
GetImage(System.Collections.Generic.IReadOnlyList`1<MediaBrowser.Model.IO.FileSystemMetadata>,System.String,System.String)