< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Books.OpenPackagingFormat.EpubImageProvider
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Books/OpenPackagingFormat/EpubImageProvider.cs
Line coverage
42%
Covered lines: 3
Uncovered lines: 4
Coverable lines: 7
Total lines: 120
Line coverage: 42.8%
Branch coverage
0%
Covered branches: 0
Total branches: 2
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 1/29/2026 - 12:13:32 AM Line coverage: 42.8% (3/7) Branch coverage: 0% (0/2) Total lines: 120 1/29/2026 - 12:13:32 AM Line coverage: 42.8% (3/7) Branch coverage: 0% (0/2) Total lines: 120

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Name()100%210%
Supports(...)100%11100%
GetImage(...)0%620%

File(s)

/srv/git/jellyfin/MediaBrowser.Providers/Books/OpenPackagingFormat/EpubImageProvider.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using System.IO.Compression;
 5using System.Threading;
 6using System.Threading.Tasks;
 7using System.Xml;
 8using MediaBrowser.Controller.Entities;
 9using MediaBrowser.Controller.Providers;
 10using MediaBrowser.Model.Entities;
 11using Microsoft.Extensions.Logging;
 12
 13namespace MediaBrowser.Providers.Books.OpenPackagingFormat
 14{
 15    /// <summary>
 16    /// Provides the primary image for EPUB items that have embedded covers.
 17    /// </summary>
 18    public class EpubImageProvider : IDynamicImageProvider
 19    {
 20        private readonly ILogger<EpubImageProvider> _logger;
 21
 22        /// <summary>
 23        /// Initializes a new instance of the <see cref="EpubImageProvider"/> class.
 24        /// </summary>
 25        /// <param name="logger">Instance of the <see cref="ILogger{EpubImageProvider}"/> interface.</param>
 26        public EpubImageProvider(ILogger<EpubImageProvider> logger)
 27        {
 2128            _logger = logger;
 2129        }
 30
 31        /// <inheritdoc />
 032        public string Name => "EPUB Metadata";
 33
 34        /// <inheritdoc />
 35        public bool Supports(BaseItem item)
 36        {
 5937            return item is Book;
 38        }
 39
 40        /// <inheritdoc />
 41        public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
 42        {
 43            yield return ImageType.Primary;
 44        }
 45
 46        /// <inheritdoc />
 47        public Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken)
 48        {
 049            if (string.Equals(Path.GetExtension(item.Path), ".epub", StringComparison.OrdinalIgnoreCase))
 50            {
 051                return GetFromZip(item, cancellationToken);
 52            }
 53
 054            return Task.FromResult(new DynamicImageResponse { HasImage = false });
 55        }
 56
 57        private async Task<DynamicImageResponse> LoadCover(ZipArchive epub, XmlDocument opf, string opfRootDirectory, Ca
 58        {
 59            var utilities = new OpfReader<EpubImageProvider>(opf, _logger);
 60            var coverReference = utilities.ReadCoverPath(opfRootDirectory);
 61            if (coverReference == null)
 62            {
 63                return new DynamicImageResponse { HasImage = false };
 64            }
 65
 66            var cover = coverReference.Value;
 67            var coverFile = epub.GetEntry(cover.Path);
 68
 69            if (coverFile == null)
 70            {
 71                return new DynamicImageResponse { HasImage = false };
 72            }
 73
 74            var memoryStream = new MemoryStream();
 75
 76            var coverStream = await coverFile.OpenAsync(cancellationToken).ConfigureAwait(false);
 77            await using (coverStream.ConfigureAwait(false))
 78            {
 79                await coverStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
 80            }
 81
 82            memoryStream.Position = 0;
 83
 84            var response = new DynamicImageResponse { HasImage = true, Stream = memoryStream };
 85            response.SetFormatFromMimeType(cover.MimeType);
 86
 87            return response;
 88        }
 89
 90        private async Task<DynamicImageResponse> GetFromZip(BaseItem item, CancellationToken cancellationToken)
 91        {
 92            using var epub = await ZipFile.OpenReadAsync(item.Path, cancellationToken).ConfigureAwait(false);
 93
 94            var opfFilePath = EpubUtils.ReadContentFilePath(epub);
 95            if (opfFilePath == null)
 96            {
 97                return new DynamicImageResponse { HasImage = false };
 98            }
 99
 100            var opfRootDirectory = Path.GetDirectoryName(opfFilePath);
 101            if (opfRootDirectory == null)
 102            {
 103                return new DynamicImageResponse { HasImage = false };
 104            }
 105
 106            var opfFile = epub.GetEntry(opfFilePath);
 107            if (opfFile == null)
 108            {
 109                return new DynamicImageResponse { HasImage = false };
 110            }
 111
 112            using var opfStream = await opfFile.OpenAsync(cancellationToken).ConfigureAwait(false);
 113
 114            var opfDocument = new XmlDocument();
 115            opfDocument.Load(opfStream);
 116
 117            return await LoadCover(epub, opfDocument, opfRootDirectory, cancellationToken).ConfigureAwait(false);
 118        }
 119    }
 120}