2021年1月19日星期二

Why won't calibre read the metadata on a recreated .epub file?

I am writing a program that takes an .epub file, unzips it, edits the content.opt file to add custom metadata, then zip the contents to create a new .epub file. I am using calibre as both my e-reader and my .epub editor, since calibre makes it very easy to edit both the metadata for an .epub as well as the contents of an .epub file.

I am able to successfully create a new .epub file. I have tested this new file can be read both with calibre and my Kobo e-reader.

However, none of the metadata from the original .epub file transfers over to the new .epub file. Additionally I am unable to edit the .epub file in calibre. When I try I get the error "No META-INF/container.xml in epub". I have tried using multiple .epub files and I get the same results and errors.

Unzipped, the contents of the original .epub file is as follows:

META/INF    ↳container.xml  content.opf  mimetype  pages_styles.css  [title]_split_000.xhtml  [title]_split_001.xhtml  .....  [title]_split_012.xhtml  [title]_split_013.xhtml  stylesheet.css  toc.ncx  

The unzipped directory for the newly created .epub file is identical to the original. Running diff -r -q /[title]_original /[title]_recreated produces no output, which would indicate they are in fact identical. So I am unsure how calibre can read one file and not read another. The error seems to indicate that calibre is somehow unable to find the META-INF/container.xml file, which is used to tell an e-reader where metadata is being stored in the directory.

Note: I am not editing any content for the original .epub during the unzipping or zipping process until I am able to figure out what is happening.

EDIT: Including the code I used below. I am running the command go run main.go zip.go in the directory with the two go files and the .epub file [title]:

main.go

package main    import (  // "log"  // "strings"  )    type FileLocations struct {      src  string      ext  string      dest string  }    func main() {        fileName := "[title]"      temp := FileLocations{          src:  fileName,          ext:  ".epub",          dest: fileName,      }        // Unzip the zip/epub file      UnzipHelper(temp.src, temp.ext, temp.dest)        // Zip the modified directory      ZipHelper(temp.src, temp.ext)  }    func UnzipHelper(src string, ext string, dest string) error {      _, err := Unzip(src, ext, dest)      if err != nil {          return err      }      return nil  }    func ZipHelper(src string, ext string) error {      err := Zip(src, ext)      if err != nil {          return err      }      return nil  }    

zip.go

package main    import (      "archive/zip"      "fmt"      "io"      "log"      "os"      "path/filepath"      "strings"  )    func Unzip(src string, ext string, dest string) ([]string, error) {        file := src + ext        var filenames []string        r, err := zip.OpenReader(file)      if err != nil {          return filenames, err      }      defer r.Close()        for _, f := range r.File {            // Store filename/path for returning and using later on          fpath := filepath.Join(dest, f.Name)            // Check for ZipSlip          if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {              return filenames, fmt.Errorf("%s: illegal file path", fpath)          }            filenames = append(filenames, fpath)            if f.FileInfo().IsDir() {              // Make Folder              os.MkdirAll(fpath, os.ModePerm)              continue          }            // Make File          if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {              return filenames, err          }            outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())          if err != nil {              return filenames, err          }            rc, err := f.Open()          if err != nil {              return filenames, err          }            _, err = io.Copy(outFile, rc)            // Close the file without defer to close before next iteration of loop          outFile.Close()          rc.Close()            if err != nil {              return filenames, err          }      }        // Remove zip file so it can be recreated later      os.Remove(file)        return filenames, nil  }    func Zip(filename string, ext string) error {      // Creates .epub file      file, err := os.Create(filename + ext)      if err != nil {          log.Fatal("os.Create(filename) error: ", err)      }      defer file.Close()        w := zip.NewWriter(file)      defer w.Close()        walker := func(path string, info os.FileInfo, err error) error {          fmt.Println("Crawling: " + path)          if err != nil {              return err          }          if info.IsDir() {              return nil          }          file, err := os.Open(path)          if err != nil {              return err          }          defer file.Close()            f, err := w.Create(path)          if err != nil {              return err          }            _, err = io.Copy(f, file)          if err != nil {              return err          }            return nil      }        err = filepath.Walk(filename, walker)      if err != nil {          log.Fatal("filepath.Walk error: ", err)      }      return err  }  
https://stackoverflow.com/questions/65799618/why-wont-calibre-read-the-metadata-on-a-recreated-epub-file January 20, 2021 at 05:13AM

没有评论:

发表评论