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 }