package org.rr.jeborker.metadata;
import static org.rr.commons.utils.StringUtil.EMPTY;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.xml.namespace.QName;
import nl.siegmann.epublib.domain.Author;
import nl.siegmann.epublib.domain.Book;
import nl.siegmann.epublib.domain.Date.Event;
import nl.siegmann.epublib.domain.Identifier;
import nl.siegmann.epublib.domain.Meta;
import nl.siegmann.epublib.domain.Metadata;
import nl.siegmann.epublib.domain.Relator;
import nl.siegmann.epublib.domain.Resource;
import nl.siegmann.epublib.domain.Resources;
import org.apache.commons.io.Charsets;
import org.rr.commons.log.LoggerFactory;
import org.rr.commons.mufs.IResourceHandler;
import org.rr.commons.utils.StringUtil;
import org.rr.jeborker.db.item.EbookPropertyItem;
class EPubLibMetadataReader extends AEpubMetadataHandler implements IMetadataReader {
EPubLibMetadataReader(IResourceHandler ebookResourceHandler) {
super(ebookResourceHandler);
}
@Override
public List<MetadataProperty> readMetadata() {
final IResourceHandler ebookResourceHandler = getEbookResource().get(0);
try {
final Book epub = readBook(ebookResourceHandler.getContentInputStream(), ebookResourceHandler, true);
final Metadata metadata = epub.getMetadata();
final List<MetadataProperty> metadataList = this.createMetadataList(epub, metadata);
return metadataList;
} catch (Throwable e) {
LoggerFactory.logWarning(getClass(), "Could not read metadata for epub " + ebookResourceHandler, e);
} finally {
ebookResourceHandler.dispose();
}
return new ArrayList<MetadataProperty>(0);
}
/**
* Read all metadata entries from the given {@link Metadata} instance into {@link EpubLibMetadataProperty}.
* @param metadata The metadata instance where the entries read from.
* @return All available metadata from teh given {@link Metadata} instance.
* @throws IOException
*/
private List<MetadataProperty> createMetadataList(final Book epub, final Metadata metadata) throws IOException {
final ArrayList<MetadataProperty> result = new ArrayList<MetadataProperty>() {
@Override
public boolean add(MetadataProperty e) {
List<Object> values = e.getValues();
if(!isListEmpty(values)) {
return super.add(e);
}
return false;
}
private boolean isListEmpty(List<Object> values) {
if(values != null && !values.isEmpty()) {
for(Object value : values) {
if(!StringUtil.isEmpty(value != null ? String.valueOf(value) : null)) {
return false;
}
}
}
return true;
}
};
List<Author> authors = metadata.getAuthors();
for (Author author : authors) {
EpubLibMetadataProperty<Author> epubLibMetadataProperty = new EpubLibMetadataProperty<>(EPUB_METADATA_TYPES.AUTHOR.getName() , (author.getFirstname() + " " + author.getLastname()).trim(), author);
result.add(epubLibMetadataProperty);
}
List<String> titles = metadata.getTitles();
for (String title : titles) {
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.TITLE.getName(), title, null));
}
List<String> descriptions = metadata.getDescriptions();
for (String description : descriptions) {
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.DESCRIPTION.getName(), description, null));
}
List<String> publishers = metadata.getPublishers();
for (String publisher : publishers) {
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.PUBLISHER.getName(), publisher, null));
}
List<String> rights = metadata.getRights();
for (String right : rights) {
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.RIGHTS.getName(), right, null));
}
List<String> subjects = metadata.getSubjects();
for (String subject : subjects) {
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.SUBJECT.getName(), subject, null));
}
List<String> types = metadata.getTypes();
for (String type : types) {
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.TYPE.getName(), type, null));
}
List<Author> contributors = metadata.getContributors();
for (Author contributor : contributors) {
Relator relator = contributor.getRelator();
if(relator != null && relator.getName().equalsIgnoreCase("CREATOR")) {
result.add(new EpubLibMetadataProperty<Author>(EPUB_METADATA_TYPES.CREATOR.getName(), contributor.getFirstname() + contributor.getLastname(), contributor));
} else {
result.add(new EpubLibMetadataProperty<Author>(EPUB_METADATA_TYPES.CONTRIBUTOR.getName(), contributor.getFirstname() + contributor.getLastname(), contributor));
}
}
List<nl.siegmann.epublib.domain.Date> dates = metadata.getDates();
for (nl.siegmann.epublib.domain.Date date : dates) {
Event event = date.getEvent();
if(event != null && Event.PUBLICATION.equals(event)) {
result.add(new EpubLibMetadataProperty<nl.siegmann.epublib.domain.Date>(EPUB_METADATA_TYPES.PUBLICATION_DATE.getName(), date.getValue(), date));
} else if(event != null && Event.CREATION.equals(event)) {
result.add(new EpubLibMetadataProperty<nl.siegmann.epublib.domain.Date>(EPUB_METADATA_TYPES.CREATION_DATE.getName(), date.getValue(), date));
} else if(event != null && Event.MODIFICATION.equals(event)) {
result.add(new EpubLibMetadataProperty<nl.siegmann.epublib.domain.Date>(EPUB_METADATA_TYPES.MODIFICATION_DATE.getName(), date.getValue(), date));
} else {
result.add(new EpubLibMetadataProperty<nl.siegmann.epublib.domain.Date>(EPUB_METADATA_TYPES.DATE.getName(), date.getValue(), date));
}
}
List<Identifier> identifiers = metadata.getIdentifiers();
for (Identifier identifier : identifiers) {
EpubLibMetadataProperty<Identifier> epubLibMetadataProperty;
if("uuid".equalsIgnoreCase(identifier.getScheme())) {
epubLibMetadataProperty = new EpubLibMetadataProperty<>(EPUB_METADATA_TYPES.UUID.getName(), identifier.getValue(), identifier);
} else if("isbn".equalsIgnoreCase(identifier.getScheme())) {
epubLibMetadataProperty = new EpubLibMetadataProperty<>(EPUB_METADATA_TYPES.ISBN.getName(), identifier.getValue(), identifier);
} else {
epubLibMetadataProperty = new EpubLibMetadataProperty<>(EPUB_METADATA_TYPES.IDENTIFIER.getName(), identifier.getValue(), identifier);
}
result.add(epubLibMetadataProperty);
}
Map<QName, String> otherProperties = metadata.getOtherProperties();
for (Map.Entry<QName, String> e : otherProperties.entrySet()){
QName key = e.getKey();
String value = e.getValue();
result.add(new EpubLibMetadataProperty<QName>(key.getPrefix() + ":" + key.getLocalPart(), value, key));
}
List<Meta> otherMeta = metadata.getOtherMeta();
for(Meta meta : otherMeta) {
//no need to read the cover meta entry. It's already provided by the Reader and added later.
if(IMetadataReader.COMMON_METADATA_TYPES.COVER.getName().equalsIgnoreCase(meta.getName())) {
continue;
}
result.add(new EpubLibMetadataProperty<Meta>(meta.getName(), meta.getContent(), meta));
}
String format = metadata.getFormat();
if(format != null && !format.isEmpty()) {
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.FORMAT.getName(), format, null));
}
String language = metadata.getLanguage();
if(language != null && !language.isEmpty()) {
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.LANGUAGE.getName(), language, null));
}
final Resource coverImage = epub.getCoverImage();
final byte[] data;
if(coverImage != null) {
data = coverImage.getData();
} else {
data = searchCoverImage(epub);
}
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.COVER.getName(), data, null));
return new ArrayList<MetadataProperty>(result);
}
/**
* Searches the given {@link Book} for an image which seems to be the cover image.
* @return The desired cover image bytes or <code>null</code> if no cover could be found.
*/
private byte[] searchCoverImage(Book epub) {
Resources resources = epub.getResources();
Collection<String> allHrefs = resources.getAllHrefs();
for(String href : allHrefs) {
if(new File(href).getName().toLowerCase().contains("cover") && (href.endsWith(".jpg") || href.endsWith(".jpeg"))) {
Resource resourcerByHref = resources.getByHref(href);
if(resourcerByHref != null) {
try {
byte[] data = resourcerByHref.getData();
if(data != null && data.length > 0) {
return resourcerByHref.getData();
}
} catch (IOException e) {
break;
}
}
}
}
return null;
}
@Override
public void fillEbookPropertyItem(final List<MetadataProperty> metadataProperties, final EbookPropertyItem item) {
item.clearMetadata();
for(MetadataProperty metadataProperty : metadataProperties) {
for(EPUB_METADATA_TYPES type : EPUB_METADATA_TYPES.values()) {
if(type.getName().equals(metadataProperty.getName())) {
type.fillItem(metadataProperty, item);
break;
}
}
}
}
@Override
public List<MetadataProperty> getSupportedMetadata() {
final ArrayList<MetadataProperty> result = new ArrayList<>();
Author author = new Author(EMPTY);
author.setRelator(Relator.AUTHOR);
result.add(new EpubLibMetadataProperty<Author>(EPUB_METADATA_TYPES.AUTHOR.getName(), EMPTY, author));
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.TITLE.getName(), EMPTY));
Meta ageSuggestion = new Meta(EPUB_METADATA_TYPES.JB_AGE_SUGGESTION.getName(), EMPTY);
result.add(new EpubLibMetadataProperty<Meta>(ageSuggestion.getName(), EMPTY, ageSuggestion));
Meta keywords = new Meta(EPUB_METADATA_TYPES.JB_KEYWORDS.getName(), EMPTY);
result.add(new EpubLibMetadataProperty<Meta>(keywords.getName(), EMPTY, keywords));
Meta rating = new Meta(EPUB_METADATA_TYPES.CALIBRE_RATING.getName(), EMPTY);
result.add(new EpubLibMetadataProperty<Meta>(rating.getName(), EMPTY, rating));
Meta seriesIndex = new Meta(EPUB_METADATA_TYPES.CALIBRE_SERIES_INDEX.getName(), EMPTY);
result.add(new EpubLibMetadataProperty<Meta>(seriesIndex.getName(), EMPTY, seriesIndex));
Meta seriesName = new Meta(EPUB_METADATA_TYPES.CALIBRE_SERIES.getName(), EMPTY);
result.add(new EpubLibMetadataProperty<Meta>(seriesName.getName(), EMPTY, seriesName));
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.SUBJECT.getName(), EMPTY));
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.PUBLISHER.getName(), EMPTY));
Identifier identifier = new Identifier("uuid", UUID.randomUUID().toString());
result.add(new EpubLibMetadataProperty<Identifier>(EPUB_METADATA_TYPES.UUID.getName(), identifier.getValue(), identifier));
Identifier isbn = new Identifier("isbn", EMPTY);
result.add(new EpubLibMetadataProperty<Identifier>(EPUB_METADATA_TYPES.ISBN.getName(), EMPTY, isbn));
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.RIGHTS.getName(), EMPTY));
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.LANGUAGE.getName(), EMPTY));
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.DESCRIPTION.getName(), EMPTY));
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.DATE.getName(), EMPTY));
Author creator = new Author(EMPTY);
creator.setRelator(Relator.CREATOR);
result.add(new EpubLibMetadataProperty<Author>(EPUB_METADATA_TYPES.CREATOR.getName(), EMPTY, creator));
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.TYPE.getName(), EMPTY));
Author contributor = new Author(EMPTY);
contributor.setRelator(Relator.CONTRIBUTOR);
result.add(new EpubLibMetadataProperty<Author>(EPUB_METADATA_TYPES.CONTRIBUTOR.getName(), EMPTY, contributor));
result.add(new EpubLibMetadataProperty<Void>(EPUB_METADATA_TYPES.FORMAT.getName(), EMPTY));
return result;
}
@Override
public String getPlainMetadata() {
try {
final byte[] containerXmlData = getContainerOPF(getEbookResource().get(0));
return new String(containerXmlData, Charsets.UTF_8);
} catch (Exception e) {
LoggerFactory.logWarning(this, "Could not get plain metadata for " + getEbookResource(), e);
}
return null;
}
@Override
public String getPlainMetadataMime() {
return "text/xml";
}
@Override
public List<MetadataProperty> getMetadataByType(boolean create, List<MetadataProperty> props, IMetadataReader.COMMON_METADATA_TYPES type) {
MetadataProperty newProperty;
switch(type) {
case AUTHOR:
Author author = new Author(EMPTY);
author.setRelator(Relator.AUTHOR);
newProperty = new EpubLibMetadataProperty<>(EPUB_METADATA_TYPES.AUTHOR.getName(), EMPTY, author);
break;
case TITLE:
newProperty = new EpubLibMetadataProperty<>(EPUB_METADATA_TYPES.TITLE.getName(), EMPTY);
break;
case GENRE:
newProperty = new EpubLibMetadataProperty<>(EPUB_METADATA_TYPES.SUBJECT.getName(), EMPTY);
break;
case SERIES_NAME:
Meta seriesName = new Meta(EPUB_METADATA_TYPES.CALIBRE_SERIES.getName(), EMPTY);
newProperty = new EpubLibMetadataProperty<>(seriesName.getName(), EMPTY, seriesName);
break;
case RATING:
Meta ratingName = new Meta(EPUB_METADATA_TYPES.CALIBRE_RATING.getName(), EMPTY);
newProperty = new EpubLibMetadataProperty<>(ratingName.getName(), EMPTY, ratingName);
break;
case AGE_SUGGESTION:
Meta ageSuggestionName = new Meta(EPUB_METADATA_TYPES.JB_AGE_SUGGESTION.getName(), EMPTY);
newProperty = new EpubLibMetadataProperty<>(ageSuggestionName.getName(), EMPTY, ageSuggestionName);
break;
case DESCRIPTION:
newProperty = new EpubLibMetadataProperty<>(EPUB_METADATA_TYPES.DESCRIPTION.getName(), EMPTY);
break;
case ISBN:
Identifier isbn = new Identifier("isbn", EMPTY);
newProperty = new EpubLibMetadataProperty<>(EPUB_METADATA_TYPES.ISBN.getName(), EMPTY, isbn);
break;
case LANGUAGE:
newProperty = new EpubLibMetadataProperty<>(EPUB_METADATA_TYPES.LANGUAGE.getName(), EMPTY);
break;
case COVER:
newProperty = new EpubLibMetadataProperty<>(EPUB_METADATA_TYPES.COVER.getName(), null, null);
break;
default: newProperty = null;
}
final ArrayList<MetadataProperty> result = new ArrayList<>();
if(newProperty != null) {
for(MetadataProperty prop : props) {
if(prop.getName().equalsIgnoreCase(newProperty.getName())) {
result.add(prop);
}
}
}
if(create && result.isEmpty() && newProperty != null) {
result.add(newProperty);
}
return result;
}
}