Select Git revision
class-wp-customize-widgets.php
refine.go 3.22 KiB
package liber
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"log"
"os"
)
func (db *Database) RefineFunc(dir string, chooser MetadataChooserFunc) func(*Book) error {
storage := NewFileStorage(dir)
refiners := defaultMetadataRefiners
return func(book *Book) error {
if err := refineMetadata(book, "", storage, refiners, chooser); err == nil {
log.Printf("%s: updated metadata", book.Id)
return db.PutBook(book)
}
return nil
}
}
// ListBooks writes IDs of books that match any of a series of
// functions to an io.Writer.
func (db *Database) ListBooks(w io.Writer, matchFuncs ...func(book *Book) bool) error {
return db.onAllBooks(func(book *Book) error {
ok := false
for _, f := range matchFuncs {
if f(book) {
ok = true
break
}
}
if ok {
fmt.Fprintf(w, "%s\n", book.Id)
}
return nil
})
}
// WithBookIDs calls a function on books whose IDs are read from the
// specified io.Reader.
func (db *Database) WithBookIDs(r io.Reader, f func(book *Book) error) error {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
id := ParseID(string(bytes.TrimSpace(scanner.Bytes())))
book, err := db.GetBook(id)
if err != nil {
log.Printf("error: no such book %s", id)
continue
}
if err := f(book); err != nil {
log.Printf("error: %s: %v", id, err)
// Stop?
}
}
return scanner.Err()
}
// Local path is a hint for generating the cover image.
func findCoverImage(book *Book, path string, refiners []MetadataRefiner, storage *FileStorage) error {
if book.CoverPath != "" {
return nil
}
localCoverPath := path + ".cover.png"
for _, refiner := range refiners {
if coverData, err := refiner.GetBookCover(book.Metadata); err == nil {
f, err := os.Create(storage.Abs(localCoverPath))
if err != nil {
return err
}
f.Write(coverData)
f.Close()
book.CoverPath = localCoverPath
return nil
}
}
// Not finding a cover image is not an error.
return nil
}
// Note: the Book may not have an ID assigned yet.
// Local path is a UI hint for the chooser.
func refineMetadata(book *Book, path string, storage *FileStorage, refiners []MetadataRefiner, chooser MetadataChooserFunc) error {
meta := book.Metadata
// Only run remote checks if the metadata isn't complete.
if !meta.Complete() {
// Integrate metadata using the refiners. We check them all,
// and merge their results into the metadata object. The user
// is prompted if a choice is necessary. Search for a book
// cover only until one is found.
for _, refiner := range refiners {
candidates, err := refiner.Lookup(meta)
if err == nil && len(candidates) > 0 {
if len(candidates) == 1 {
log.Printf("found match from %s: %s", refiner.Name(), candidates[0].String())
meta.Merge(candidates[0])
} else if chooser != nil {
if userchoice := chooser(path, candidates); userchoice != nil {
meta.Merge(userchoice)
}
}
}
}
}
// Check if the book metadata looks ok. If not, don't even
// bother looking for a cover image.
if !meta.Sufficient() {
return errors.New("insufficient metadata")
}
// Errors finding/saving cover images are not fatal.
if err := findCoverImage(book, path, refiners, storage); err != nil {
log.Printf("Error saving cover image: %v", err)
}
return nil
}