diff --git a/openlibrary.go b/openlibrary.go new file mode 100644 index 0000000000000000000000000000000000000000..580cc55501a546f70bd19af128670db50cbfff65 --- /dev/null +++ b/openlibrary.go @@ -0,0 +1,160 @@ +package liber + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "regexp" + "strings" +) + +type openLibraryRefiner struct{} + +func (r *openLibraryRefiner) Name() string { + return "openlibrary" +} + +type olSearchResults struct { + Docs []olSearchResult `json:"docs"` +} + +type olSearchResult struct { + Title string `json:"title"` + Author string `json:"author_name"` + Date string `json:"first_publish_year"` + OLKey string `json:"key"` +} + +type olAuthor struct { + Name string `json:"name"` +} + +type olPublisher olAuthor + +type olExcerpt struct { + Text string `json:"text"` +} + +type olMetadata struct { + Key string `json:"key"` + Title string `json:"title"` + Identifiers map[string][]string `json:"identifiers"` + Authors []*olAuthor `json:"authors"` + Excerpts []*olExcerpt `json:"excerpts"` + Publishers []*olPublisher `json:"publishers"` + PublishDate string `json:"publish_date"` + Cover map[string]string `json:"cover"` + Language []string +} + +var dateRx = regexp.MustCompile(`(\d{4})`) + +func (m *olMetadata) toMetadata() *Metadata { + out := &Metadata{ + Title: m.Title, + Language: m.Language, + } + for _, a := range m.Authors { + out.Creator = append(out.Creator, a.Name) + } + for _, p := range m.Publishers { + out.Publisher = append(out.Publisher, p.Name) + } + for _, attr := range []string{"isbn_10", "isbn_13"} { + if isbn, ok := m.Identifiers[attr]; ok { + out.ISBN = append(out.ISBN, isbn...) + } + } + if match := dateRx.FindString(m.PublishDate); match != "" { + out.Date = match + } + for _, e := range m.Excerpts { + out.Description = e.Text + break + } + return out +} + +var olSemaphore = make(chan bool, 3) + +func openlibraryQuery(uri string, out interface{}) error { + olSemaphore <- true + defer func() { + <-olSemaphore + }() + + resp, err := http.Get(uri) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + return fmt.Errorf("HTTP status %s", resp.Status) + } + if err := json.NewDecoder(resp.Body).Decode(out); err != nil { + return err + } + return nil +} + +// Return book information given an ID. +func (r *openLibraryRefiner) queryBibKeys(bibkeys []string) ([]*Metadata, error) { + values := make(url.Values) + values.Set("bibkeys", strings.Join(bibkeys, ",")) + values.Set("jscmd", "data") + values.Set("format", "json") + uri := fmt.Sprintf("https://openlibrary.org/api/books?%s", values.Encode()) + + var result map[string]*olMetadata + if err := openlibraryQuery(uri, &result); err != nil { + return nil, err + } + + var out []*Metadata + for _, m := range result { + out = append(out, m.toMetadata()) + } + return out, nil +} + +// Find a book given one or more ISBN numbers. +func (r *openLibraryRefiner) queryISBN(isbn []string) ([]*Metadata, error) { + var bibkeys []string + for _, i := range isbn { + bibkeys = append(bibkeys, fmt.Sprintf("ISBN:%s", i)) + } + return r.queryBibKeys(bibkeys) +} + +// Find a book using title/author. +func (r *openLibraryRefiner) queryTitleAuthor(m *Metadata) ([]*Metadata, error) { + values := make(url.Values) + values.Set("title", m.Title) + if len(m.Creator) > 0 { + values.Set("author", m.Creator[0]) + } + uri := fmt.Sprintf("https://openlibrary.org/search.json?%s", values.Encode()) + + var result olSearchResults + if err := openlibraryQuery(uri, &result); err != nil { + return nil, err + } + + var bibkeys []string + for _, doc := range result.Docs { + bibkeys = append(bibkeys, fmt.Sprintf("OLID:%s", doc.OLKey)) + } + return r.queryBibKeys(bibkeys) +} + +func (r *openLibraryRefiner) Lookup(m *Metadata) ([]*Metadata, error) { + if len(m.ISBN) > 0 { + return r.queryISBN(m.ISBN) + } + return r.queryTitleAuthor(m) +} + +func (r *openLibraryRefiner) GetBookCover(m *Metadata) ([]byte, error) { + return nil, nil +} diff --git a/update.go b/update.go index d16f7d782b1aff1effbfb6b685c90134621d58af..a00d30f6b7abea3c5c6ccb64720472e57e201fe0 100644 --- a/update.go +++ b/update.go @@ -306,6 +306,7 @@ func (db *Database) Update(dir string, chooser MetadataChooserFunc) { // Check Google Books when the metadata is not // sufficient to fully describe the book. refiners: []MetadataRefiner{ + //&openLibraryRefiner{}, &googleBooksRefiner{}, }, }