Skip to content

Commit

Permalink
Refactor opf metadata reader for readability
Browse files Browse the repository at this point in the history
The image provider for ePuBs read the opf metadata file and various code
segments in the OpfReader class were duplicated code.

This commit refactors the opf reader for readability. Additionally, it
moves opf related code from the ePuB image provider into the opf reader.
  • Loading branch information
carif committed Jan 16, 2022
1 parent 20c78b2 commit 8233ac6
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 184 deletions.
24 changes: 14 additions & 10 deletions Jellyfin.Plugin.Bookshelf/Providers/BookProviderFromOpf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,24 @@ public bool HasChanged(BaseItem item, IDirectoryService directoryService)
public Task<MetadataResult<Book>> GetMetadata(ItemInfo info, IDirectoryService directoryService, CancellationToken cancellationToken)
{
var path = GetXmlFile(info.Path).FullName;
var result = new MetadataResult<Book>();

try
{
var item = new Book();
result.HasMetadata = true;
result.Item = item;
ReadOpfData(result, path, cancellationToken);
var result = ReadOpfData(path, cancellationToken);

if (result is null)
{
return Task.FromResult(new MetadataResult<Book> { HasMetadata = false });
}
else
{
return Task.FromResult(result);
}
}
catch (FileNotFoundException)
{
result.HasMetadata = false;
return Task.FromResult(new MetadataResult<Book> { HasMetadata = false });
}

return Task.FromResult(result);
}

private FileSystemMetadata GetXmlFile(string path)
Expand All @@ -85,14 +88,15 @@ private FileSystemMetadata GetXmlFile(string path)
return file.Exists ? file : _fileSystem.GetFileInfo(Path.Combine(directoryPath, CalibreOpfFile));
}

private void ReadOpfData(MetadataResult<Book> bookResult, string metaFile, CancellationToken cancellationToken)
private MetadataResult<Book> ReadOpfData(string metaFile, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();

var doc = new XmlDocument();
doc.Load(metaFile);

OpfReader.ReadOpfData(bookResult, doc, _logger, cancellationToken);
var utilities = new OpfReader<BookProviderFromOpf>(doc, _logger);
return utilities.ReadOpfData(cancellationToken);
}
}
}
111 changes: 13 additions & 98 deletions Jellyfin.Plugin.Bookshelf/Providers/Epub/EpubMetadataImageProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;

namespace Jellyfin.Plugin.Bookshelf.Providers.Epub
{
Expand All @@ -17,8 +18,16 @@ namespace Jellyfin.Plugin.Bookshelf.Providers.Epub
/// </summary>
public class EpubMetadataImageProvider : IDynamicImageProvider
{
private const string DcNamespace = @"http://purl.org/dc/elements/1.1/";
private const string OpfNamespace = @"http://www.idpf.org/2007/opf";
private readonly ILogger<EpubMetadataImageProvider> _logger;

/// <summary>
/// Initializes a new instance of the <see cref="EpubMetadataImageProvider"/> class.
/// </summary>
/// <param name="logger">Instance of the <see cref="ILogger{EpubMetadataImageProvider}"/> interface.</param>
public EpubMetadataImageProvider(ILogger<EpubMetadataImageProvider> logger)
{
_logger = logger;
}

/// <inheritdoc />
public string Name => "Epub Metadata";
Expand Down Expand Up @@ -46,92 +55,10 @@ public Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, Cancel
return Task.FromResult(new DynamicImageResponse { HasImage = false });
}

private bool IsValidImage(string? mimeType)
{
return !string.IsNullOrEmpty(mimeType)
&& !string.IsNullOrWhiteSpace(MimeTypes.ToExtension(mimeType));
}

private EpubCover? ReadManifestItem(XmlNode manifestNode, string opfRootDirectory)
{
var href = manifestNode.Attributes?["href"]?.Value;
var mediaType = manifestNode.Attributes?["media-type"]?.Value;

if (string.IsNullOrEmpty(href)
|| string.IsNullOrEmpty(mediaType)
|| !IsValidImage(mediaType))
{
return null;
}

var coverPath = Path.Combine(opfRootDirectory, href);
return new EpubCover(mediaType, coverPath);
}

private EpubCover? ReadCoverPath(XmlDocument opf, string opfRootDirectory)
{
var namespaceManager = new XmlNamespaceManager(opf.NameTable);
namespaceManager.AddNamespace("dc", DcNamespace);
namespaceManager.AddNamespace("opf", OpfNamespace);

var coverImagePropertyNode = opf.SelectSingleNode("//opf:item[@properties='cover-image']", namespaceManager);
if (coverImagePropertyNode is not null)
{
var coverImageProperty = ReadManifestItem(coverImagePropertyNode, opfRootDirectory);
if (coverImageProperty != null)
{
return coverImageProperty;
}
}

var coverIdNode = opf.SelectSingleNode("//opf:item[@id='cover']", namespaceManager);
if (coverIdNode is not null)
{
var coverId = ReadManifestItem(coverIdNode, opfRootDirectory);
if (coverId != null)
{
return coverId;
}
}

var coverImageIdNode = opf.SelectSingleNode("//opf:item[@id='cover-image']", namespaceManager);
if (coverImageIdNode is not null)
{
var coverImageId = ReadManifestItem(coverImageIdNode, opfRootDirectory);
if (coverImageId != null)
{
return coverImageId;
}
}

var metaCoverImage = opf.SelectSingleNode("//opf:meta[@name='cover']", namespaceManager);
var content = metaCoverImage?.Attributes?["content"]?.Value;
if (string.IsNullOrEmpty(content) || metaCoverImage is null)
{
return null;
}

var coverPath = Path.Combine("Images", content);
var coverFileManifest = opf.SelectSingleNode($"//opf:item[@href='{coverPath}']", namespaceManager);
var mediaType = coverFileManifest?.Attributes?["media-type"]?.Value;
if (coverFileManifest?.Attributes is not null
&& !string.IsNullOrEmpty(mediaType) && IsValidImage(mediaType))
{
return new EpubCover(mediaType, Path.Combine(opfRootDirectory, coverPath));
}

var coverFileIdManifest = opf.SelectSingleNode($"//opf:item[@id='{content}']", namespaceManager);
if (coverFileIdManifest is not null)
{
return ReadManifestItem(coverFileIdManifest, opfRootDirectory);
}

return null;
}

private async Task<DynamicImageResponse> LoadCover(ZipArchive epub, XmlDocument opf, string opfRootDirectory)
{
var coverRef = ReadCoverPath(opf, opfRootDirectory);
var utilities = new OpfReader<EpubMetadataImageProvider>(opf, _logger);
var coverRef = utilities.ReadCoverPath(opfRootDirectory);
if (coverRef == null)
{
return new DynamicImageResponse { HasImage = false };
Expand Down Expand Up @@ -193,17 +120,5 @@ private Task<DynamicImageResponse> GetFromZip(BaseItem item)

return LoadCover(epub, opfDocument, opfRootDirectory);
}

private readonly struct EpubCover
{
public EpubCover(string coverMimeType, string coverPath)
{
(MimeType, Path) = (coverMimeType, coverPath);
}

public string MimeType { get; }

public string Path { get; }
}
}
}
28 changes: 15 additions & 13 deletions Jellyfin.Plugin.Bookshelf/Providers/Epub/EpubMetadataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,22 @@ public Task<MetadataResult<Book>> GetMetadata(
CancellationToken cancellationToken)
{
var path = GetEpubFile(info.Path)?.FullName;
var result = new MetadataResult<Book>();

if (path == null)
if (path is null)
{
result.HasMetadata = false;
return Task.FromResult(new MetadataResult<Book> { HasMetadata = false });
}

var result = ReadEpubAsZip(path, cancellationToken);

if (result is null)
{
return Task.FromResult(new MetadataResult<Book> { HasMetadata = false });
}
else
{
var item = new Book();
result.HasMetadata = true;
result.Item = item;
ReadEpubAsZip(result, path, cancellationToken);
return Task.FromResult(result);
}

return Task.FromResult(result);
}

private FileSystemMetadata? GetEpubFile(string path)
Expand All @@ -74,28 +75,29 @@ public Task<MetadataResult<Book>> GetMetadata(
return fileInfo;
}

private void ReadEpubAsZip(MetadataResult<Book> result, string path, CancellationToken cancellationToken)
private MetadataResult<Book>? ReadEpubAsZip(string path, CancellationToken cancellationToken)
{
using var epub = ZipFile.OpenRead(path);

var opfFilePath = EpubUtils.ReadContentFilePath(epub);
if (opfFilePath == null)
{
return;
return null;
}

var opf = epub.GetEntry(opfFilePath);
if (opf == null)
{
return;
return null;
}

using var opfStream = opf.Open();

var opfDocument = new XmlDocument();
opfDocument.Load(opfStream);

OpfReader.ReadOpfData(result, opfDocument, _logger, cancellationToken);
var utilities = new OpfReader<EpubMetadataProvider>(opfDocument, _logger);
return utilities.ReadOpfData(cancellationToken);
}
}
}
Loading

0 comments on commit 8233ac6

Please sign in to comment.