hugo-medialog/internal/books/api_googlebooks.go

189 lines
4.9 KiB
Go
Raw Normal View History

2024-10-10 14:13:40 +02:00
package books
import (
"encoding/json"
"fmt"
"hugo-medialog/utils"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
)
const googleBooksAPIBaseURL = "https://www.googleapis.com/books/v1/volumes?q=intitle:%s&key=%s"
// Function to search for a book by title
func SearchBookByTitle(title string, book *Book) error {
apiKey := os.Getenv("GOOGLE_BOOKS_API_KEY")
url := fmt.Sprintf(googleBooksAPIBaseURL, url.QueryEscape(title), apiKey)
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return err
}
items := result["items"].([]interface{})
if len(items) == 0 {
return fmt.Errorf("Book %s not found", title)
}
// Get the ISBN from any of the results
isbn, err := extractISBNFromResults(items)
if err != nil {
fmt.Printf("ISBN not found\n", isbn)
} else {
book.ISBN = isbn
}
bookInfo := items[0].(map[string]interface{})["volumeInfo"].(map[string]interface{})
book.ID = items[0].(map[string]interface{})["id"].(string)
book.Title = bookInfo["title"].(string)
book.Author = bookInfo["authors"].([]interface{})[0].(string)
2024-10-10 14:31:03 +02:00
book.Link = items[0].(map[string]interface{})["selfLink"].(string)
2024-10-10 14:13:40 +02:00
if pageCount, ok := bookInfo["pageCount"].(float64); ok {
book.Pages = int(pageCount)
}
if publishedDate, ok := bookInfo["publishedDate"].(string); ok {
if len(publishedDate) >= 4 {
book.Year, _ = strconv.Atoi(publishedDate[:4])
}
}
book.Image = getBookCoverByTitle(title)
if err != nil {
fmt.Printf("Image not found: %s\n", err)
}
return nil
}
func extractISBNFromResults(items []interface{}) (string, error) {
// Loop over all the results and returns first ISBN found (ISBN_13 or
// ISBN_10)
for _, item := range items {
bookInfo := item.(map[string]interface{})["volumeInfo"].(map[string]interface{})
industryIdentifiers, ok := bookInfo["industryIdentifiers"].([]interface{})
if !ok {
continue
}
var isbn string
for _, identifier := range industryIdentifiers {
id := identifier.(map[string]interface{})
// Primero intentamos con ISBN_13
if id["type"] == "ISBN_13" {
isbn = id["identifier"].(string)
return isbn, nil
} else if id["type"] == "ISBN_10" && isbn == "" {
// Si no hay ISBN_13, intentamos con ISBN_10
isbn = id["identifier"].(string)
return isbn, nil
}
}
}
return "", fmt.Errorf("no ISBN found in any result")
}
func getBookCoverByISBN(isbn string) string {
openLibraryURL := fmt.Sprintf("https://covers.openlibrary.org/b/isbn/%s-L.jpg", isbn)
if checkImageExists(openLibraryURL) {
return openLibraryURL
}
amazonURL := fmt.Sprintf("https://images-na.ssl-images-amazon.com/images/P/%s.jpg", isbn)
if checkImageExists(amazonURL) {
return amazonURL
}
return ""
}
func getBookCoverByTitle(title string) string {
apiKey := os.Getenv("GOOGLE_CUSTOM_SEARCH_ENGINE_API_KEY")
cx := os.Getenv("GOOGLE_CX")
searchURL := fmt.Sprintf("https://www.googleapis.com/customsearch/v1?q=%s+book+cover&key=%s&searchType=image&num=1&cx=%s", url.QueryEscape(title), apiKey, cx)
resp, err := http.Get(searchURL)
if err != nil {
fmt.Printf("Error fetching cover from Google Images API: %s\n", err)
return "default-cover.jpg"
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Printf("Error fetching cover, status code: %d\n", resp.StatusCode)
return "default-cover.jpg"
}
var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
fmt.Printf("Error decoding JSON response: %s\n", err)
return "default-cover.jpg"
}
if items, ok := result["items"].([]interface{}); ok && len(items) > 0 {
if link, ok := items[0].(map[string]interface{})["link"].(string); ok {
return link
}
}
return "default-cover.jpg"
}
func checkImageExists(imageURL string) bool {
resp, err := http.Head(imageURL)
if err != nil || resp.StatusCode != http.StatusOK {
return false
}
return true
}
func DownloadImage(url, slug string) error {
imageDir := filepath.Join(os.Getenv("MARKDOWN_OUTPUT_BOOKS_DIR"), os.Getenv("IMAGES_OUTPUT_DIR"))
if err := utils.CreateDirIfNotExists(imageDir); err != nil {
return err
}
filename := fmt.Sprintf("%s.jpg", slug)
filePath := filepath.Join(imageDir, filename)
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("error downloading image: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("non-200 response while downloading image: %d", resp.StatusCode)
}
file, err := os.Create(filePath)
if err != nil {
return fmt.Errorf("error creating image file: %w", err)
}
defer file.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("error reading image data: %w", err)
}
_, err = file.Write(body)
if err != nil {
return fmt.Errorf("error writing image data: %w", err)
}
fmt.Printf(" - Image saved successfully at: %s\n", filePath)
return nil
}