You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Readarr/src/NzbDrone.Core/MediaFiles/EpubTag/Readers/PackageReader.cs

287 lines
11 KiB

using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Threading.Tasks;
using System.Xml.Linq;
using VersOne.Epub.Schema;
using VersOne.Epub.Utils;
namespace VersOne.Epub.Internal
{
public static class PackageReader
{
public static async Task<EpubPackage> ReadPackageAsync(ZipArchive epubArchive, string rootFilePath)
{
var rootFileEntry = epubArchive.GetEntry(rootFilePath);
if (rootFileEntry == null)
{
throw new Exception("EPUB parsing error: root file not found in archive.");
}
XDocument containerDocument;
using (var containerStream = rootFileEntry.Open())
{
containerDocument = await XmlUtils.LoadDocumentAsync(containerStream).ConfigureAwait(false);
}
XNamespace opfNamespace = "http://www.idpf.org/2007/opf";
var packageNode = containerDocument.Element(opfNamespace + "package");
if (packageNode == null)
{
throw new Exception("Invalid epub file");
}
var epubVersionValue = packageNode.Attribute("version").Value;
EpubVersion epubVersion;
switch (epubVersionValue)
{
case "1.0":
case "2.0":
epubVersion = EpubVersion.EPUB_2;
break;
case "3.0":
epubVersion = EpubVersion.EPUB_3_0;
break;
case "3.1":
epubVersion = EpubVersion.EPUB_3_1;
break;
default:
throw new Exception($"Unsupported EPUB version: {epubVersionValue}.");
}
var result = new EpubPackage();
result.EpubVersion = epubVersion;
var metadataNode = packageNode.Element(opfNamespace + "metadata");
if (metadataNode == null)
{
throw new Exception("EPUB parsing error: metadata not found in the package.");
}
var metadata = ReadMetadata(metadataNode, result.EpubVersion);
result.Metadata = metadata;
return result;
}
private static EpubMetadata ReadMetadata(XElement metadataNode, EpubVersion epubVersion)
{
var result = new EpubMetadata
{
Titles = new List<string>(),
Creators = new List<EpubMetadataCreator>(),
Subjects = new List<string>(),
Publishers = new List<string>(),
Contributors = new List<EpubMetadataContributor>(),
Dates = new List<EpubMetadataDate>(),
Types = new List<string>(),
Formats = new List<string>(),
Identifiers = new List<EpubMetadataIdentifier>(),
Sources = new List<string>(),
Languages = new List<string>(),
Relations = new List<string>(),
Coverages = new List<string>(),
Rights = new List<string>(),
MetaItems = new List<EpubMetadataMeta>()
};
foreach (var metadataItemNode in metadataNode.Elements())
{
var innerText = metadataItemNode.Value;
switch (metadataItemNode.GetLowerCaseLocalName())
{
case "title":
result.Titles.Add(innerText);
break;
case "creator":
var creator = ReadMetadataCreator(metadataItemNode);
result.Creators.Add(creator);
break;
case "subject":
result.Subjects.Add(innerText);
break;
case "description":
result.Description = innerText;
break;
case "publisher":
result.Publishers.Add(innerText);
break;
case "contributor":
var contributor = ReadMetadataContributor(metadataItemNode);
result.Contributors.Add(contributor);
break;
case "date":
var date = ReadMetadataDate(metadataItemNode);
result.Dates.Add(date);
break;
case "type":
result.Types.Add(innerText);
break;
case "format":
result.Formats.Add(innerText);
break;
case "identifier":
var identifier = ReadMetadataIdentifier(metadataItemNode);
result.Identifiers.Add(identifier);
break;
case "source":
result.Sources.Add(innerText);
break;
case "language":
result.Languages.Add(innerText);
break;
case "relation":
result.Relations.Add(innerText);
break;
case "coverage":
result.Coverages.Add(innerText);
break;
case "rights":
result.Rights.Add(innerText);
break;
case "meta":
if (epubVersion == EpubVersion.EPUB_2)
{
var meta = ReadMetadataMetaVersion2(metadataItemNode);
result.MetaItems.Add(meta);
}
else if (epubVersion == EpubVersion.EPUB_3_0 || epubVersion == EpubVersion.EPUB_3_1)
{
var meta = ReadMetadataMetaVersion3(metadataItemNode);
result.MetaItems.Add(meta);
}
break;
}
}
return result;
}
private static EpubMetadataCreator ReadMetadataCreator(XElement metadataCreatorNode)
{
var result = new EpubMetadataCreator();
foreach (var metadataCreatorNodeAttribute in metadataCreatorNode.Attributes())
{
var attributeValue = metadataCreatorNodeAttribute.Value;
switch (metadataCreatorNodeAttribute.GetLowerCaseLocalName())
{
case "role":
result.Role = attributeValue;
break;
case "file-as":
result.FileAs = attributeValue;
break;
}
}
result.Creator = metadataCreatorNode.Value;
return result;
}
private static EpubMetadataContributor ReadMetadataContributor(XElement metadataContributorNode)
{
var result = new EpubMetadataContributor();
foreach (var metadataContributorNodeAttribute in metadataContributorNode.Attributes())
{
var attributeValue = metadataContributorNodeAttribute.Value;
switch (metadataContributorNodeAttribute.GetLowerCaseLocalName())
{
case "role":
result.Role = attributeValue;
break;
case "file-as":
result.FileAs = attributeValue;
break;
}
}
result.Contributor = metadataContributorNode.Value;
return result;
}
private static EpubMetadataDate ReadMetadataDate(XElement metadataDateNode)
{
var result = new EpubMetadataDate();
var eventAttribute = metadataDateNode.Attribute(metadataDateNode.Name.Namespace + "event");
if (eventAttribute != null)
{
result.Event = eventAttribute.Value;
}
result.Date = metadataDateNode.Value;
return result;
}
private static EpubMetadataIdentifier ReadMetadataIdentifier(XElement metadataIdentifierNode)
{
var result = new EpubMetadataIdentifier();
foreach (var metadataIdentifierNodeAttribute in metadataIdentifierNode.Attributes())
{
var attributeValue = metadataIdentifierNodeAttribute.Value;
switch (metadataIdentifierNodeAttribute.GetLowerCaseLocalName())
{
case "id":
result.Id = attributeValue;
break;
case "opf:scheme":
result.Scheme = attributeValue;
break;
}
}
result.Identifier = metadataIdentifierNode.Value;
return result;
}
private static EpubMetadataMeta ReadMetadataMetaVersion2(XElement metadataMetaNode)
{
var result = new EpubMetadataMeta();
foreach (var metadataMetaNodeAttribute in metadataMetaNode.Attributes())
{
var attributeValue = metadataMetaNodeAttribute.Value;
switch (metadataMetaNodeAttribute.GetLowerCaseLocalName())
{
case "name":
result.Name = attributeValue;
break;
case "content":
result.Content = attributeValue;
break;
}
}
return result;
}
private static EpubMetadataMeta ReadMetadataMetaVersion3(XElement metadataMetaNode)
{
var result = new EpubMetadataMeta();
foreach (var metadataMetaNodeAttribute in metadataMetaNode.Attributes())
{
var attributeValue = metadataMetaNodeAttribute.Value;
switch (metadataMetaNodeAttribute.GetLowerCaseLocalName())
{
case "id":
result.Id = attributeValue;
break;
case "refines":
result.Refines = attributeValue;
break;
case "property":
result.Property = attributeValue;
break;
case "scheme":
result.Scheme = attributeValue;
break;
}
}
result.Content = metadataMetaNode.Value;
return result;
}
}
}