From 6963d9588080f23d351a95051696b6c692afc2c5 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sat, 22 Nov 2025 16:25:59 +0900 Subject: extract local metadata from OPF and EPUB files --- .../Books/OpenPackagingFormat/EpubImageProvider.cs | 118 +++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 MediaBrowser.Providers/Books/OpenPackagingFormat/EpubImageProvider.cs (limited to 'MediaBrowser.Providers/Books/OpenPackagingFormat/EpubImageProvider.cs') diff --git a/MediaBrowser.Providers/Books/OpenPackagingFormat/EpubImageProvider.cs b/MediaBrowser.Providers/Books/OpenPackagingFormat/EpubImageProvider.cs new file mode 100644 index 0000000000..33d2823de6 --- /dev/null +++ b/MediaBrowser.Providers/Books/OpenPackagingFormat/EpubImageProvider.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Providers.Books.OpenPackagingFormat +{ + /// + /// Provides the primary image for EPUB items that have embedded covers. + /// + public class EpubImageProvider : IDynamicImageProvider + { + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + public EpubImageProvider(ILogger logger) + { + _logger = logger; + } + + /// + public string Name => "EPUB Metadata"; + + /// + public bool Supports(BaseItem item) + { + return item is Book; + } + + /// + public IEnumerable GetSupportedImages(BaseItem item) + { + yield return ImageType.Primary; + } + + /// + public Task GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken) + { + if (string.Equals(Path.GetExtension(item.Path), ".epub", StringComparison.OrdinalIgnoreCase)) + { + return GetFromZip(item); + } + + return Task.FromResult(new DynamicImageResponse { HasImage = false }); + } + + private async Task LoadCover(ZipArchive epub, XmlDocument opf, string opfRootDirectory) + { + var utilities = new OpfReader(opf, _logger); + var coverReference = utilities.ReadCoverPath(opfRootDirectory); + if (coverReference == null) + { + return new DynamicImageResponse { HasImage = false }; + } + + var cover = coverReference.Value; + var coverFile = epub.GetEntry(cover.Path); + + if (coverFile == null) + { + return new DynamicImageResponse { HasImage = false }; + } + + var memoryStream = new MemoryStream(); + using (var coverStream = coverFile.Open()) + { + await coverStream.CopyToAsync(memoryStream).ConfigureAwait(false); + } + + memoryStream.Position = 0; + + var response = new DynamicImageResponse { HasImage = true, Stream = memoryStream }; + response.SetFormatFromMimeType(cover.MimeType); + + return response; + } + + private async Task GetFromZip(BaseItem item) + { + using var epub = ZipFile.OpenRead(item.Path); + + var opfFilePath = EpubUtils.ReadContentFilePath(epub); + if (opfFilePath == null) + { + return new DynamicImageResponse { HasImage = false }; + } + + var opfRootDirectory = Path.GetDirectoryName(opfFilePath); + if (opfRootDirectory == null) + { + return new DynamicImageResponse { HasImage = false }; + } + + var opfFile = epub.GetEntry(opfFilePath); + if (opfFile == null) + { + return new DynamicImageResponse { HasImage = false }; + } + + using var opfStream = opfFile.Open(); + + var opfDocument = new XmlDocument(); + opfDocument.Load(opfStream); + + return await LoadCover(epub, opfDocument, opfRootDirectory).ConfigureAwait(false); + } + } +} -- cgit v1.2.3 From b97f5b809daa5e64bc54eb6534ed4c4f69ff6cd9 Mon Sep 17 00:00:00 2001 From: Gladtbam <49943258+Gladtbam@users.noreply.github.com> Date: Wed, 28 Jan 2026 10:40:12 +0800 Subject: fix: Resolve CA1849/CA2007 synchronous IO in EpubImageProvider (#16124) --- .../Books/OpenPackagingFormat/EpubImageProvider.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'MediaBrowser.Providers/Books/OpenPackagingFormat/EpubImageProvider.cs') diff --git a/MediaBrowser.Providers/Books/OpenPackagingFormat/EpubImageProvider.cs b/MediaBrowser.Providers/Books/OpenPackagingFormat/EpubImageProvider.cs index 33d2823de6..69cae77628 100644 --- a/MediaBrowser.Providers/Books/OpenPackagingFormat/EpubImageProvider.cs +++ b/MediaBrowser.Providers/Books/OpenPackagingFormat/EpubImageProvider.cs @@ -48,13 +48,13 @@ namespace MediaBrowser.Providers.Books.OpenPackagingFormat { if (string.Equals(Path.GetExtension(item.Path), ".epub", StringComparison.OrdinalIgnoreCase)) { - return GetFromZip(item); + return GetFromZip(item, cancellationToken); } return Task.FromResult(new DynamicImageResponse { HasImage = false }); } - private async Task LoadCover(ZipArchive epub, XmlDocument opf, string opfRootDirectory) + private async Task LoadCover(ZipArchive epub, XmlDocument opf, string opfRootDirectory, CancellationToken cancellationToken) { var utilities = new OpfReader(opf, _logger); var coverReference = utilities.ReadCoverPath(opfRootDirectory); @@ -72,9 +72,11 @@ namespace MediaBrowser.Providers.Books.OpenPackagingFormat } var memoryStream = new MemoryStream(); - using (var coverStream = coverFile.Open()) + + var coverStream = await coverFile.OpenAsync(cancellationToken).ConfigureAwait(false); + await using (coverStream.ConfigureAwait(false)) { - await coverStream.CopyToAsync(memoryStream).ConfigureAwait(false); + await coverStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false); } memoryStream.Position = 0; @@ -85,9 +87,9 @@ namespace MediaBrowser.Providers.Books.OpenPackagingFormat return response; } - private async Task GetFromZip(BaseItem item) + private async Task GetFromZip(BaseItem item, CancellationToken cancellationToken) { - using var epub = ZipFile.OpenRead(item.Path); + using var epub = await ZipFile.OpenReadAsync(item.Path, cancellationToken).ConfigureAwait(false); var opfFilePath = EpubUtils.ReadContentFilePath(epub); if (opfFilePath == null) @@ -107,12 +109,12 @@ namespace MediaBrowser.Providers.Books.OpenPackagingFormat return new DynamicImageResponse { HasImage = false }; } - using var opfStream = opfFile.Open(); + using var opfStream = await opfFile.OpenAsync(cancellationToken).ConfigureAwait(false); var opfDocument = new XmlDocument(); opfDocument.Load(opfStream); - return await LoadCover(epub, opfDocument, opfRootDirectory).ConfigureAwait(false); + return await LoadCover(epub, opfDocument, opfRootDirectory, cancellationToken).ConfigureAwait(false); } } } -- cgit v1.2.3