opf.go 2.64 KB
Newer Older
ale's avatar
ale committed
1 2 3 4
package liber

import (
	"encoding/xml"
5
	"errors"
ale's avatar
ale committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
	"io"
	"path/filepath"
	"strings"
)

type opfIdentifier struct {
	Scheme string `xml:"http://www.idpf.org/2007/opf scheme,attr"`
	Value  string `xml:",chardata"`
}

type opfMeta struct {
	XMLName     xml.Name        `xml:"http://www.idpf.org/2007/opf metadata"`
	Title       string          `xml:"http://purl.org/dc/elements/1.1/ title"`
	Date        string          `xml:"http://purl.org/dc/elements/1.1/ date"`
	Description string          `xml:"http://purl.org/dc/elements/1.1/ description"`
	Creator     []string        `xml:"http://purl.org/dc/elements/1.1/ creator"`
	Language    []string        `xml:"http://purl.org/dc/elements/1.1/ language"`
	Publisher   []string        `xml:"http://purl.org/dc/elements/1.1/ publisher"`
	Identifier  []opfIdentifier `xml:"http://purl.org/dc/elements/1.1/ identifier"`
}

type opfPackage struct {
	Meta *opfMeta
}

func (o *opfMeta) ToMetadata() *Metadata {
	m := &Metadata{
		Title:       o.Title,
		Description: o.Description,
		Publisher:   o.Publisher,
		Language:    o.Language,
	}
	if o.Date != "" && o.Date != "0101-01-01T00:00:00+00:00" {
		m.Date = toYear(o.Date)
	}
	for _, c := range o.Creator {
		for _, cc := range strings.Split(c, ",") {
			m.Creator = append(m.Creator, strings.TrimSpace(cc))
		}
	}
	var uuid string
	for _, id := range o.Identifier {
		if id.Scheme == "ISBN" {
			m.ISBN = append(m.ISBN, id.Value)
		} else if id.Scheme == "uuid" {
			uuid = id.Value
		}
	}
	m.Sources = []MetadataSource{{
		Name: "opf",
		ID:   uuid,
	}}
	return m
}

func opfParse(r io.Reader) (*Metadata, error) {
	var opf opfPackage
	if err := xml.NewDecoder(r).Decode(&opf); err != nil {
		return nil, err
	}
	return opf.Meta.ToMetadata(), nil
}

69 70
func opfOpen(storage *FileStorage, path string) (*Metadata, error) {
	file, err := storage.Open(path)
ale's avatar
ale committed
71 72 73 74 75 76 77
	if err != nil {
		return nil, err
	}
	defer file.Close()
	return opfParse(file)
}

78 79
func replaceExt(path, ext string) string {
	return strings.TrimSuffix(path, filepath.Ext(path)) + ext
ale's avatar
ale committed
80
}
81 82 83 84

type opfProvider struct{}

func (p *opfProvider) Lookup(storage *FileStorage, path, filetype string) (*Metadata, error) {
85 86 87
	paths := []string{
		filepath.Join(filepath.Dir(path), "metadata.opf"),
		replaceExt(path, ".opf"),
88
	}
89
	for _, opfpath := range paths {
90
		if m, err := opfOpen(storage, opfpath); err == nil {
91 92
			return m, err
		}
93
	}
94
	return nil, errors.New("no OPF metadata found")
95 96 97
}

func (p *opfProvider) GetBookCover(storage *FileStorage, path string) (string, error) {
98
	coverPath := filepath.Join(filepath.Dir(path), "cover.jpg")
99 100 101 102 103 104 105 106 107
	if storage.Exists(coverPath) {
		return coverPath, nil
	}
	return "", nil
}

func (p *opfProvider) Name() string {
	return "opf"
}