< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Books.ComicInfo.InternalComicInfoProvider
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Books/ComicInfo/InternalComicInfoProvider.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 39
Coverable lines: 39
Total lines: 120
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 20
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 6/11/2026 - 12:16:04 AM Line coverage: 0% (0/39) Branch coverage: 0% (0/20) Total lines: 120 6/11/2026 - 12:16:04 AM Line coverage: 0% (0/39) Branch coverage: 0% (0/20) Total lines: 120

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
ReadMetadata()0%2040%
HasItemChanged(...)0%2040%
LoadXml()0%7280%
GetComicBookFile(...)0%2040%

File(s)

/srv/git/jellyfin/MediaBrowser.Providers/Books/ComicInfo/InternalComicInfoProvider.cs

#LineLine coverage
 1using System;
 2using System.IO.Compression;
 3using System.Threading;
 4using System.Threading.Tasks;
 5using System.Xml.Linq;
 6using MediaBrowser.Controller.Entities;
 7using MediaBrowser.Controller.Providers;
 8using MediaBrowser.Model.IO;
 9using Microsoft.Extensions.Logging;
 10
 11namespace MediaBrowser.Providers.Books.ComicInfo;
 12
 13/// <summary>
 14/// Handles metadata for comics which is saved as an XML document inside the comic itself.
 15/// </summary>
 16public class InternalComicInfoProvider : IComicProvider
 17{
 18    private readonly IFileSystem _fileSystem;
 19    private readonly ILogger<InternalComicInfoProvider> _logger;
 20
 21    /// <summary>
 22    /// Initializes a new instance of the <see cref="InternalComicInfoProvider"/> class.
 23    /// </summary>
 24    /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
 25    /// <param name="logger">Instance of the <see cref="ILogger{InternalComicInfoProvider}"/> interface.</param>
 26    public InternalComicInfoProvider(IFileSystem fileSystem, ILogger<InternalComicInfoProvider> logger)
 27    {
 028        _logger = logger;
 029        _fileSystem = fileSystem;
 030    }
 31
 32    /// <inheritdoc />
 33    public async ValueTask<MetadataResult<Book>> ReadMetadata(ItemInfo info, IDirectoryService directoryService, Cancell
 34    {
 035        var comicInfoXml = await LoadXml(info, cancellationToken).ConfigureAwait(false);
 36
 037        if (comicInfoXml is null)
 38        {
 039            _logger.LogInformation("Could not load ComicInfo metadata for {Path} from XML file. No internal XML in comic
 040            return new MetadataResult<Book> { HasMetadata = false };
 41        }
 42
 043        var book = ComicInfoReader.ReadComicBookMetadata(comicInfoXml);
 44
 045        if (book is null)
 46        {
 047            return new MetadataResult<Book> { HasMetadata = false };
 48        }
 49
 050        var metadataResult = new MetadataResult<Book> { Item = book, HasMetadata = true };
 51
 052        ComicInfoReader.ReadPeopleMetadata(comicInfoXml, metadataResult);
 053        ComicInfoReader.ReadCultureInfoInto(comicInfoXml, "ComicInfo/LanguageISO", cultureInfo => metadataResult.ResultL
 54
 055        return metadataResult;
 056    }
 57
 58    /// <inheritdoc />
 59    public bool HasItemChanged(BaseItem item)
 60    {
 061        var file = GetComicBookFile(item.Path);
 62
 063        if (file is null)
 64        {
 065            return false;
 66        }
 67
 068        return file.Exists && _fileSystem.GetLastWriteTimeUtc(file) > item.DateLastSaved;
 69    }
 70
 71    private async Task<XDocument?> LoadXml(ItemInfo info, CancellationToken cancellationToken)
 72    {
 073        var path = GetComicBookFile(info.Path)?.FullName;
 74
 075        if (path is null)
 76        {
 077            return null;
 78        }
 79
 80        try
 81        {
 82            // open the comic archive and try to get the ComicInfo.xml entry
 083            using var comicBookFile = await ZipFile.OpenReadAsync(path, cancellationToken).ConfigureAwait(false);
 084            var container = comicBookFile.GetEntry(ComicInfoReader.ComicRackMetaFile);
 85
 086            if (container is null)
 87            {
 088                return null;
 89            }
 90
 091            using var containerStream = await container.OpenAsync(cancellationToken).ConfigureAwait(false);
 092            var comicInfoXml = XDocument.LoadAsync(containerStream, LoadOptions.None, cancellationToken);
 93
 094            return await comicInfoXml.ConfigureAwait(false);
 95        }
 096        catch (Exception e)
 97        {
 098            _logger.LogError(e, "could not load internal XML from {Path}", path);
 099            return null;
 100        }
 0101    }
 102
 103    private FileSystemMetadata? GetComicBookFile(string path)
 104    {
 0105        var fileInfo = _fileSystem.GetFileSystemInfo(path);
 106
 0107        if (fileInfo.IsDirectory)
 108        {
 0109            return null;
 110        }
 111
 112        // only parse files that are known to have internal metadata
 0113        if (!string.Equals(fileInfo.Extension, ".cbz", StringComparison.OrdinalIgnoreCase))
 114        {
 0115            return null;
 116        }
 117
 0118        return fileInfo;
 119    }
 120}