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 }