189 lines
4.9 KiB
Go
189 lines
4.9 KiB
Go
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)
|
|
book.Link = items[0].(map[string]interface{})["selfLink"].(string)
|
|
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
|
|
}
|