< 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
7%
Covered lines: 3
Uncovered lines: 38
Coverable lines: 41
Total lines: 121
Line coverage: 7.3%
Branch coverage
0%
Covered branches: 0
Total branches: 14
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/16/2026 - 12:15:55 AM Line coverage: 7.3% (3/41) Branch coverage: 0% (0/14) Total lines: 12010/29/2026 - 12:17:11 AM Line coverage: 7.3% (3/41) Branch coverage: 0% (0/14) Total lines: 121 5/16/2026 - 12:15:55 AM Line coverage: 7.3% (3/41) Branch coverage: 0% (0/14) Total lines: 12010/29/2026 - 12:17:11 AM Line coverage: 7.3% (3/41) Branch coverage: 0% (0/14) Total lines: 121

Coverage delta

Coverage delta 1 -1

Metrics

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

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        {
 2728            _logger = logger;
 2729        }
 30
 31        /// <inheritdoc />
 032        public string Name => "EPUB Metadata";
 33
 34        /// <inheritdoc />
 35        public bool Supports(BaseItem item)
 36        {
 10737            return item is Book;
 38        }
 39
 40        /// <inheritdoc />
 41        public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
 42        {
 043            yield return ImageType.Primary;
 044        }
 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        {
 059            var utilities = new OpfReader<EpubImageProvider>(opf, _logger);
 060            var coverReference = utilities.ReadCoverPath(opfRootDirectory);
 061            if (coverReference == null)
 62            {
 063                return new DynamicImageResponse { HasImage = false };
 64            }
 65
 066            var cover = coverReference.Value;
 67            // ZIP entry names always use '/', but the cover path is built with Path.Combine, which uses '\' on Windows
 068            var coverFile = epub.GetEntry(cover.Path.Replace('\\', '/'));
 69
 070            if (coverFile == null)
 71            {
 072                return new DynamicImageResponse { HasImage = false };
 73            }
 74
 075            var memoryStream = new MemoryStream();
 76
 077            var coverStream = await coverFile.OpenAsync(cancellationToken).ConfigureAwait(false);
 078            await using (coverStream.ConfigureAwait(false))
 79            {
 080                await coverStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
 81            }
 82
 083            memoryStream.Position = 0;
 84
 085            var response = new DynamicImageResponse { HasImage = true, Stream = memoryStream };
 086            response.SetFormatFromMimeType(cover.MimeType);
 87
 088            return response;
 089        }
 90
 91        private async Task<DynamicImageResponse> GetFromZip(BaseItem item, CancellationToken cancellationToken)
 92        {
 093            using var epub = await ZipFile.OpenReadAsync(item.Path, cancellationToken).ConfigureAwait(false);
 94
 095            var opfFilePath = EpubUtils.ReadContentFilePath(epub);
 096            if (opfFilePath == null)
 97            {
 098                return new DynamicImageResponse { HasImage = false };
 99            }
 100
 0101            var opfRootDirectory = Path.GetDirectoryName(opfFilePath);
 0102            if (opfRootDirectory == null)
 103            {
 0104                return new DynamicImageResponse { HasImage = false };
 105            }
 106
 0107            var opfFile = epub.GetEntry(opfFilePath);
 0108            if (opfFile == null)
 109            {
 0110                return new DynamicImageResponse { HasImage = false };
 111            }
 112
 0113            using var opfStream = await opfFile.OpenAsync(cancellationToken).ConfigureAwait(false);
 114
 0115            var opfDocument = new XmlDocument();
 0116            opfDocument.Load(opfStream);
 117
 0118            return await LoadCover(epub, opfDocument, opfRootDirectory, cancellationToken).ConfigureAwait(false);
 0119        }
 120    }
 121}