Jordan
Jordan

Reputation: 177

How to get higher res image from Google Books Api?

When I try to get a higher resolution image from the google books API it only gives me the small thumbnail size that is 128 X 209 and I can't get a larger image

This is my url:

if let url = URL(string: "https://www.googleapis.com/books/v1/volumes?q=colleen+hoover")

My Structs:

    struct Book: Identifiable, Codable {
    let id = UUID()
    let volumeInfo: VolumeInfo
}

struct VolumeInfo: Codable {
    let title, publishedDate: String?
    let authors: [String]?
    let publisher, description: String?
    let imageLinks: ImageLinks?
    let averageRating: Double?
    
}

struct ApiResponse: Codable {
    let kind: String
    let totalItems: Int
    let items: [Book]
}

struct ImageLinks: Codable {
    let smallThumbnail, thumbnail: String
}

and this is how I'm downloading the image from the url

extension UIImageView {
    func downloaded(from url: URL, contentMode mode: ContentMode = .scaleAspectFit) {
        contentMode = mode
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard
                let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
                let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
                let data = data, error == nil,
                let image = UIImage(data: data)
            else { return }
            DispatchQueue.main.async() { [weak self] in
                self?.image = image
            }
        }.resume()
    }
    func downloaded(from link: String, contentMode mode: ContentMode = .scaleAspectFit) {
        guard let url = URL(string: link) else { return }
        downloaded(from: url, contentMode: mode)
    }
}

Upvotes: 3

Views: 2355

Answers (2)

thiago-barbosa
thiago-barbosa

Reputation: 41

Most of the time replacing zoom=1 by zoom=10 in the response coming from volumeInfo.imageLinks.thumbnail would do the trick, but it doesn't work always. Sometimes you get that "image not found" image from Google, like this one.

And when this happens there's no way around it since it probably means Google itself doesn't have a larger file to show.

I solved this with something that is a little overkill, but it works 100% of the time:

  1. I used Vision library to detect the text inside the image
  2. If the image has the words "image not found" I just replace it by my own default image.

I don't think Google will replace this 404 image anytime soon, but if it does I would need to adapt my code.

This is an example of text detection on images:

private func performOCR(on imageURL: URL, completionHandler: @escaping ([String]?, Error?) -> Void) {
    guard let image = CIImage(contentsOf: imageURL) else {
        completionHandler(nil, NSError(domain: "OCR", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to load image from URL."]))
        return
    }
    
    let handler = VNImageRequestHandler(ciImage: image, options: [:])
    
    // Create a text recognition request
    let textRecognitionRequest = VNRecognizeTextRequest { request, error in
        if let error = error {
            completionHandler(nil, error)
            return
        }
        
        guard let observations = request.results as? [VNRecognizedTextObservation],
              !observations.isEmpty else {
            completionHandler(nil, NSError(domain: "OCR", code: 2, userInfo: [NSLocalizedDescriptionKey: "No text found in the image."]))
            return
        }
        
        var detectedText: [String] = []
        for observation in observations {
            guard let topCandidate = observation.topCandidates(1).first else { continue }
            detectedText.append(topCandidate.string)
        }
        
        completionHandler(detectedText, nil)
    }
    
    textRecognitionRequest.recognitionLevel = .accurate
    textRecognitionRequest.usesLanguageCorrection = true
    
    do {
        try handler.perform([textRecognitionRequest])
    } catch {
        completionHandler(nil, error)
    }
}

And you can use this function like this:

private func googleImageNotFound(url: String, completion: @escaping (Bool) -> Void) {
    // These are the words that appears on Google's default "image not found" image
    let keywords = ["image", "not", "found"]
    
    guard let imageUrl = URL(string: url) else {
        completion(true)
        return
    }
    
    performOCR(on: imageUrl) { detectedTexts, error in
        if let error = error {
            print("OCR Error: \(error)")
            completion(true) // Return true if there's an error
        } else if let detectedTexts = detectedTexts {
            let containsWords = detectedTexts.contains { detectedText in
                keywords.allSatisfy { word in
                    detectedText.localizedCaseInsensitiveContains(word)
                }
            }
            completion(!containsWords) // Return true if words are not found
        } else {
            completion(true) // Return true if detectedTexts is nil
        }
    }
}

Don't forget to import Vision:

import Vision

Upvotes: 1

Jordan
Jordan

Reputation: 177

Ok, I figured out a way to fix this but I'm not sure if it's the best solution.

Instead of getting the image URL from the struct and using the thumbnail, I used this URL instead and just change the bookImgId variable to your book id

https://books.google.com/books/publisher/content/images/frontcover/\(bookImgId)?fife=w400-h600&source=gbs_api

This part of the URL is where you will change the image size to whatever you want

?fife=w400-h600

Upvotes: 5

Related Questions