< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.MediaInfo.EmbeddedImageProvider
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs
Line coverage
89%
Covered lines: 42
Uncovered lines: 5
Coverable lines: 47
Total lines: 245
Line coverage: 89.3%
Branch coverage
62%
Covered branches: 10
Total branches: 16
Branch coverage: 62.5%
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%210%
get_Order()100%210%
GetSupportedImages(...)100%44100%
GetImage(...)50%4.25475%
Supports(...)50%12.1860%

File(s)

/srv/git/jellyfin/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs

#LineLine coverage
 1#nullable disable
 2
 3using System;
 4using System.Collections.Generic;
 5using System.IO;
 6using System.Linq;
 7using System.Threading;
 8using System.Threading.Tasks;
 9using MediaBrowser.Controller.Entities;
 10using MediaBrowser.Controller.Entities.TV;
 11using MediaBrowser.Controller.Library;
 12using MediaBrowser.Controller.MediaEncoding;
 13using MediaBrowser.Controller.Persistence;
 14using MediaBrowser.Controller.Providers;
 15using MediaBrowser.Model.Drawing;
 16using MediaBrowser.Model.Dto;
 17using MediaBrowser.Model.Entities;
 18using MediaBrowser.Model.MediaInfo;
 19using MediaBrowser.Model.Net;
 20using Microsoft.Extensions.Logging;
 21
 22namespace MediaBrowser.Providers.MediaInfo
 23{
 24    /// <summary>
 25    /// Uses <see cref="IMediaEncoder"/> to extract embedded images.
 26    /// </summary>
 27    public class EmbeddedImageProvider : IDynamicImageProvider, IHasOrder
 28    {
 129        private static readonly string[] _primaryImageFileNames =
 130        {
 131            "poster",
 132            "folder",
 133            "cover",
 134            "default",
 135            "movie",
 136            "show"
 137        };
 38
 139        private static readonly string[] _backdropImageFileNames =
 140        {
 141            "backdrop",
 142            "background",
 143            "art"
 144        };
 45
 146        private static readonly string[] _logoImageFileNames =
 147        {
 148            "logo",
 149        };
 50
 51        private readonly IMediaSourceManager _mediaSourceManager;
 52        private readonly IMediaEncoder _mediaEncoder;
 53        private readonly ILogger<EmbeddedImageProvider> _logger;
 54
 55        /// <summary>
 56        /// Initializes a new instance of the <see cref="EmbeddedImageProvider"/> class.
 57        /// </summary>
 58        /// <param name="mediaSourceManager">The media source manager for fetching item streams and attachments.</param>
 59        /// <param name="mediaEncoder">The media encoder for extracting attached/embedded images.</param>
 60        /// <param name="logger">The logger.</param>
 61        public EmbeddedImageProvider(IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, ILogger<Embedde
 62        {
 4463            _mediaSourceManager = mediaSourceManager;
 4464            _mediaEncoder = mediaEncoder;
 4465            _logger = logger;
 4466        }
 67
 68        /// <inheritdoc />
 069        public string Name => "Embedded Image Extractor";
 70
 71        /// <inheritdoc />
 72        // Default to after internet image providers but before Screen Grabber
 073        public int Order => 99;
 74
 75        /// <inheritdoc />
 76        public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
 77        {
 678            if (item is Video)
 79            {
 280                if (item is Episode)
 81                {
 182                    return new[]
 183                    {
 184                        ImageType.Primary,
 185                    };
 86                }
 87
 188                return new[]
 189                {
 190                    ImageType.Primary,
 191                    ImageType.Backdrop,
 192                    ImageType.Logo,
 193                };
 94            }
 95
 496            return Array.Empty<ImageType>();
 97        }
 98
 99        /// <inheritdoc />
 100        public Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken)
 101        {
 16102            var video = (Video)item;
 103
 104            // No support for these
 16105            if (video.IsPlaceHolder || video.VideoType == VideoType.Dvd)
 106            {
 0107                return Task.FromResult(new DynamicImageResponse { HasImage = false });
 108            }
 109
 16110            return GetEmbeddedImage(video, type, cancellationToken);
 111        }
 112
 113        private async Task<DynamicImageResponse> GetEmbeddedImage(Video item, ImageType type, CancellationToken cancella
 114        {
 115            MediaSourceInfo mediaSource = new MediaSourceInfo
 116            {
 117                VideoType = item.VideoType,
 118                IsoType = item.IsoType,
 119                Protocol = item.PathProtocol ?? MediaProtocol.File,
 120            };
 121
 122            string[] imageFileNames = type switch
 123            {
 124                ImageType.Primary => _primaryImageFileNames,
 125                ImageType.Backdrop => _backdropImageFileNames,
 126                ImageType.Logo => _logoImageFileNames,
 127                _ => Array.Empty<string>()
 128            };
 129
 130            if (imageFileNames.Length == 0)
 131            {
 132                _logger.LogWarning("Attempted to load unexpected image type: {Type}", type);
 133                return new DynamicImageResponse { HasImage = false };
 134            }
 135
 136            // Try attachments first
 137            var attachmentStream = _mediaSourceManager.GetMediaAttachments(item.Id)
 138                .FirstOrDefault(attachment => !string.IsNullOrEmpty(attachment.FileName)
 139                    && imageFileNames.Any(name => attachment.FileName.Contains(name, StringComparison.OrdinalIgnoreCase)
 140
 141            if (attachmentStream is not null)
 142            {
 143                return await ExtractAttachment(item, attachmentStream, mediaSource, cancellationToken).ConfigureAwait(fa
 144            }
 145
 146            // Fall back to EmbeddedImage streams
 147            var imageStreams = _mediaSourceManager.GetMediaStreams(new MediaStreamQuery
 148            {
 149                ItemId = item.Id,
 150                Type = MediaStreamType.EmbeddedImage
 151            });
 152
 153            if (imageStreams.Count == 0)
 154            {
 155                // Can't extract if we don't have any EmbeddedImage streams
 156                return new DynamicImageResponse { HasImage = false };
 157            }
 158
 159            // Extract first stream containing an element of imageFileNames
 160            var imageStream = imageStreams
 161                .FirstOrDefault(stream => !string.IsNullOrEmpty(stream.Comment)
 162                    && imageFileNames.Any(name => stream.Comment.Contains(name, StringComparison.OrdinalIgnoreCase)));
 163
 164            // Primary type only: default to first image if none found by label
 165            if (imageStream is null)
 166            {
 167                if (type == ImageType.Primary)
 168                {
 169                    imageStream = imageStreams[0];
 170                }
 171                else
 172                {
 173                    // No streams matched, abort
 174                    return new DynamicImageResponse { HasImage = false };
 175                }
 176            }
 177
 178            var format = imageStream.Codec switch
 179            {
 180                "bmp" => ImageFormat.Bmp,
 181                "gif" => ImageFormat.Gif,
 182                "mjpeg" => ImageFormat.Jpg,
 183                "png" => ImageFormat.Png,
 184                "webp" => ImageFormat.Webp,
 185                _ => ImageFormat.Jpg
 186            };
 187
 188            string extractedImagePath =
 189                await _mediaEncoder.ExtractVideoImage(item.Path, item.Container, mediaSource, imageStream, imageStream.I
 190                    .ConfigureAwait(false);
 191
 192            return new DynamicImageResponse
 193            {
 194                Format = format,
 195                HasImage = true,
 196                Path = extractedImagePath,
 197                Protocol = MediaProtocol.File
 198            };
 199        }
 200
 201        private async Task<DynamicImageResponse> ExtractAttachment(Video item, MediaAttachment attachmentStream, MediaSo
 202        {
 203            var extension = string.IsNullOrEmpty(attachmentStream.MimeType)
 204                ? Path.GetExtension(attachmentStream.FileName)
 205                : MimeTypes.ToExtension(attachmentStream.MimeType);
 206
 207            ImageFormat format = extension switch
 208            {
 209                ".bmp" => ImageFormat.Bmp,
 210                ".gif" => ImageFormat.Gif,
 211                ".png" => ImageFormat.Png,
 212                ".webp" => ImageFormat.Webp,
 213                _ => ImageFormat.Jpg
 214            };
 215
 216            string extractedAttachmentPath =
 217                await _mediaEncoder.ExtractVideoImage(item.Path, item.Container, mediaSource, null, attachmentStream.Ind
 218                    .ConfigureAwait(false);
 219
 220            return new DynamicImageResponse
 221            {
 222                Format = format,
 223                HasImage = true,
 224                Path = extractedAttachmentPath,
 225                Protocol = MediaProtocol.File
 226            };
 227        }
 228
 229        /// <inheritdoc />
 230        public bool Supports(BaseItem item)
 231        {
 46232            if (item.IsShortcut)
 233            {
 0234                return false;
 235            }
 236
 46237            if (!item.IsFileProtocol)
 238            {
 0239                return false;
 240            }
 241
 46242            return item is Video video && !video.IsPlaceHolder && video.IsCompleteMedia;
 243        }
 244    }
 245}