package liber import ( "encoding/xml" "errors" "io" "os" "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 } func opfOpen(path string) (*Metadata, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() return opfParse(file) } func replaceExt(path, ext string) string { return strings.TrimSuffix(path, filepath.Ext(path)) + ext } type opfProvider struct{} func (p *opfProvider) Lookup(storage *FileStorage, path, filetype string) (*Metadata, error) { paths := []string{ filepath.Join(filepath.Dir(path), "metadata.opf"), replaceExt(path, ".opf"), } for _, opfpath := range paths { if m, err := opfOpen(storage.Abs(opfpath)); err == nil { return m, err } } return nil, errors.New("no OPF metadata found") } func (p *opfProvider) GetBookCover(storage *FileStorage, path string) (string, error) { coverPath := filepath.Join(filepath.Dir(path), "cover.jpg") if storage.Exists(coverPath) { return coverPath, nil } return "", nil } func (p *opfProvider) Name() string { return "opf" }