126 lines
2.8 KiB
Go
126 lines
2.8 KiB
Go
package bookfile
|
|
|
|
import (
|
|
"archive/zip"
|
|
"encoding/xml"
|
|
"errors"
|
|
"io"
|
|
"net/url"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
func CBZCover(f io.ReaderAt, size int64) ([]byte, string, error) {
|
|
idx, err := PageIndex(f, size)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
if len(idx) == 0 {
|
|
return nil, "", errors.New("no page images")
|
|
}
|
|
img, err := ReadEntry(f, size, idx[0])
|
|
return img, strings.ToLower(path.Ext(idx[0])), err
|
|
}
|
|
|
|
func readZipEntry(zr *zip.Reader, name string) ([]byte, error) {
|
|
for _, zf := range zr.File {
|
|
if zf.Name == name {
|
|
rc, err := zf.Open()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rc.Close()
|
|
return io.ReadAll(io.LimitReader(rc, 8<<20))
|
|
}
|
|
}
|
|
return nil, errors.New("entry not found: " + name)
|
|
}
|
|
|
|
type opfDoc struct {
|
|
XMLName xml.Name `xml:"package"`
|
|
Metadata struct {
|
|
Meta []struct {
|
|
Name string `xml:"name,attr"`
|
|
Content string `xml:"content,attr"`
|
|
} `xml:"meta"`
|
|
} `xml:"metadata"`
|
|
Manifest struct {
|
|
Items []struct {
|
|
ID string `xml:"id,attr"`
|
|
Href string `xml:"href,attr"`
|
|
Properties string `xml:"properties,attr"`
|
|
MediaType string `xml:"media-type,attr"`
|
|
} `xml:"item"`
|
|
} `xml:"manifest"`
|
|
}
|
|
|
|
func EPUBCover(f io.ReaderAt, size int64) ([]byte, string, error) {
|
|
zr, err := zip.NewReader(f, size)
|
|
if err != nil {
|
|
return nil, "", ErrNotZip
|
|
}
|
|
cont, err := readZipEntry(zr, "META-INF/container.xml")
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
var root struct {
|
|
XMLName xml.Name `xml:"container"`
|
|
RootFiles []struct {
|
|
FullPath string `xml:"full-path,attr"`
|
|
} `xml:"rootfiles>rootfile"`
|
|
}
|
|
if err := xml.Unmarshal(cont, &root); err != nil || len(root.RootFiles) == 0 {
|
|
return nil, "", errors.New("bad container.xml")
|
|
}
|
|
opfPath := root.RootFiles[0].FullPath
|
|
opfData, err := readZipEntry(zr, opfPath)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
var doc opfDoc
|
|
if err := xml.Unmarshal(opfData, &doc); err != nil {
|
|
return nil, "", errors.New("bad opf")
|
|
}
|
|
metaCoverID := ""
|
|
for _, m := range doc.Metadata.Meta {
|
|
if m.Name == "cover" {
|
|
metaCoverID = m.Content
|
|
}
|
|
}
|
|
href := ""
|
|
for _, it := range doc.Manifest.Items {
|
|
if strings.Contains(it.Properties, "cover-image") {
|
|
href = it.Href
|
|
break
|
|
}
|
|
if metaCoverID != "" && it.ID == metaCoverID {
|
|
href = it.Href
|
|
break
|
|
}
|
|
}
|
|
if href == "" {
|
|
for _, it := range doc.Manifest.Items {
|
|
if strings.HasPrefix(it.MediaType, "image/") && strings.Contains(strings.ToLower(it.ID), "cover") {
|
|
href = it.Href
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if href == "" {
|
|
return nil, "", errors.New("no cover found")
|
|
}
|
|
unescaped, e := url.PathUnescape(href)
|
|
if e == nil {
|
|
href = unescaped
|
|
}
|
|
name := path.Join(path.Dir(opfPath), href)
|
|
if name == "." {
|
|
name = href
|
|
}
|
|
img, err := readZipEntry(zr, name)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
return img, strings.ToLower(path.Ext(name)), nil
|
|
}
|