import logging import requests logger = logging.getLogger(__name__) OPENLIBRARY_BASE_URL = "https://openlibrary.org" def lookup_book_from_openlibrary(title: str, author: str | None = None) -> dict: try: response = requests.get( f"{OPENLIBRARY_BASE_URL}/search.json", params={"title": title, "limit": 5}, timeout=10, ) response.raise_for_status() search_results = response.json() except Exception as e: logger.warning(f"Could not search for {title}: {e}") return {} docs = search_results.get("docs", []) title_lower = title.lower() for work_doc in docs: found_title = work_doc.get("title", "") if found_title.lower() == title_lower: olid = work_doc.get("key", "").replace("/works/", "") if not olid: continue break else: logger.info(f"OpenLibrary: no close match for '{title}', skipping") return {} try: work_response = requests.get( f"{OPENLIBRARY_BASE_URL}/works/{olid}.json", timeout=10 ) work_response.raise_for_status() work_json = work_response.json() except Exception as e: logger.warning(f"Could not get work JSON for {olid}: {e}") return {} book_dict: dict = {"title": work_json.get("title"), "openlibrary_id": olid} description = work_json.get("description") if description: if isinstance(description, dict): book_dict["summary"] = description.get("value", "") else: book_dict["summary"] = str(description) subjects = work_json.get("subjects", []) if subjects: book_dict["genres"] = subjects[:10] covers = work_json.get("covers", []) if covers: book_dict["cover_url"] = ( f"https://covers.openlibrary.org/b/id/{covers[0]}-L.jpg" ) try: editions_response = requests.get( f"{OPENLIBRARY_BASE_URL}/works/{olid}/editions.json", timeout=10 ) if editions_response.status_code == 200: editions_data = editions_response.json() entries = editions_data.get("entries", []) if entries: edition = entries[0] isbn_list = edition.get("isbn_10", []) if isbn_list: book_dict["isbn_10"] = isbn_list[0] isbn_list = edition.get("isbn_13", []) if isbn_list: book_dict["isbn_13"] = isbn_list[0] publishers = edition.get("publishers", []) if publishers: book_dict["publisher"] = publishers[0] pub_date = edition.get("publish_date") if pub_date: if len(pub_date) >= 4: try: book_dict["first_publish_year"] = int(pub_date[:4]) except ValueError: pass pages = edition.get("number_of_pages_median") if pages: book_dict["pages"] = pages edition_covers = edition.get("covers", []) if edition_covers and not book_dict.get("cover_url"): book_dict["cover_url"] = ( f"https://covers.openlibrary.org/b/id/{edition_covers[0]}-L.jpg" ) except Exception as e: logger.warning(f"Could not get editions for {olid}: {e}") if author: book_dict["authors"] = [author] elif work_doc.get("author_name"): book_dict["authors"] = work_doc.get("author_name", []) return book_dict def lookup_author_from_openlibrary(name: str) -> dict: try: response = requests.get( f"{OPENLIBRARY_BASE_URL}/search.json", params={"author": name, "limit": 1}, timeout=10, ) response.raise_for_status() search_results = response.json() except Exception as e: logger.warning(f"Could not search for author {name}: {e}") return {} docs = search_results.get("docs", []) if not docs: return {} author_doc = docs[0] author_olid = author_doc.get("key", "").replace("/authors/", "") if not author_olid: return {} author_dict: dict = { "name": author_doc.get("name", name), "openlibrary_id": author_olid, } try: author_response = requests.get( f"{OPENLIBRARY_BASE_URL}/authors/{author_olid}.json", timeout=10 ) if author_response.status_code == 200: author_json = author_response.json() bio = author_json.get("bio") if bio: if isinstance(bio, dict): author_dict["bio"] = bio.get("value", "") else: author_dict["bio"] = str(bio) wikipedia = author_json.get("wikipedia") if wikipedia: author_dict["wikipedia_url"] = wikipedia except Exception as e: logger.warning(f"Could not get author JSON for {author_olid}: {e}") author_dict["author_headshot_url"] = ( f"https://covers.openlibrary.org/a/olid/{author_olid}-L.jpg" ) return author_dict