From 09309e20d1c6b057c20ee047d99c2ac4462b3aa1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 18 Jul 2026 05:33:03 +0000 Subject: [PATCH 1/2] Fix wrong-edition covers for five catalog titles Update coverImage URLs so Python for Everyone, Malik Java Programming, both Core Java 13th volumes, and 100 C++ Mistakes match the editions listed in the library. Co-authored-by: Muhammad Saim Shafique --- web/data/books.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/data/books.json b/web/data/books.json index 1b5fa76..e7c34e5 100644 --- a/web/data/books.json +++ b/web/data/books.json @@ -7,7 +7,7 @@ "category": "beginner", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/1uQAJEhukgO4JmJGMJpKM_jqxNLX7NM8_/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8509385-L.jpg" + "coverImage": "https://media.wiley.com/product_data/coverImage300/50/11197399/1119739950.jpg" }, { "id": "python-python-crash-course", @@ -1107,7 +1107,7 @@ "category": "beginner", "edition": "5th Edition", "driveUrl": "https://drive.google.com/file/d/1w5wT5gp7mPmUx1b-lBjdNJIaTx8YqcWF/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/7502497-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/id/6900817-L.jpg" }, { "id": "java-beginning-java-programming", @@ -1207,7 +1207,7 @@ "category": "intermediate", "edition": "13th Edition", "driveUrl": "https://drive.google.com/file/d/1Wg4jaBfblzrkjL4XIDgvnw4aY7AM-p9E/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8514927-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/isbn/9780137673629-L.jpg" }, { "id": "java-core-java-volume-ii-advanced-features", @@ -1217,7 +1217,7 @@ "category": "intermediate", "edition": "13th Edition", "driveUrl": "https://drive.google.com/file/d/1UBeNIyG0ax3F0lGFwHtNpyqO9iXDgVyE/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8508076-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1666029031i/60323250.jpg" }, { "id": "java-the-well-grounded-java-developer", @@ -2377,7 +2377,7 @@ "category": "intermediate", "edition": "2025 Edition", "driveUrl": "https://drive.google.com/file/d/1_ym2s8PFLtwnYC3BXnO0VKgw6Cop6RNd/view?usp=drive_link", - "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1585242654i/52719099.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/d/1606dd7-34f2-444e-836e-09dcd4819657/Yonts-HI.png" }, { "id": "cpp-c-concurrency-in-action", From 218942fa57a39426a9679ccf3a257ac09782e516 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 18 Jul 2026 05:47:28 +0000 Subject: [PATCH 2/2] Rematch covers for numbered editions across the catalog Replace many wrong-edition cover images using edition-strict Open Library, Manning, and Goodreads lookups, restore real covers for published reference titles, and fix known mismatches (Learning Python 5e, Learning Java 5e, etc.). Co-authored-by: Muhammad Saim Shafique --- scripts/rematch_edition_covers_strict.py | 398 +++++++++++++++++++++++ web/data/books.json | 228 ++++++------- web/lib/covers.ts | 12 +- 3 files changed, 521 insertions(+), 117 deletions(-) create mode 100644 scripts/rematch_edition_covers_strict.py diff --git a/scripts/rematch_edition_covers_strict.py b/scripts/rematch_edition_covers_strict.py new file mode 100644 index 0000000..34f8447 --- /dev/null +++ b/scripts/rematch_edition_covers_strict.py @@ -0,0 +1,398 @@ +#!/usr/bin/env python3 +"""Strict edition-aware cover rematch for numbered/year editions.""" + +from __future__ import annotations + +import json +import re +import time +import urllib.error +import urllib.parse +import urllib.request +from concurrent.futures import ThreadPoolExecutor, as_completed +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +BOOKS_PATH = ROOT / "web" / "data" / "books.json" +UA = "FreeProgrammingBooksCoverBot/2.0 (strict edition rematch)" +OL_COVER = "https://covers.openlibrary.org/b/id/{id}-L.jpg" +OL_ISBN = "https://covers.openlibrary.org/b/isbn/{isbn}-L.jpg" +MAX_WORKERS = 5 +PAUSE = 0.15 + +# Skip true online docs — they use the reference SVG. +SKIP_EDITION = re.compile( + r"online resource|official reference|official language", re.I +) + + +def load_books() -> list[dict]: + return json.loads(BOOKS_PATH.read_text(encoding="utf-8")) + + +def save_books(books: list[dict]) -> None: + BOOKS_PATH.write_text( + json.dumps(books, indent=2, ensure_ascii=False) + "\n", encoding="utf-8" + ) + + +def normalize(text: str) -> str: + text = text.lower() + text = text.replace("c++", "cpp").replace("c#", "csharp") + text = re.sub(r"[^\w\s]", " ", text) + return re.sub(r"\s+", " ", text).strip() + + +def edition_info(edition: str) -> tuple[str | None, str | None]: + """Return (kind, value) where kind is 'nth' or 'year'.""" + ed = edition or "" + m = re.search(r"(\d+)\s*(st|nd|rd|th)?\s*edition", ed, re.I) + if m: + return "nth", m.group(1) + m = re.search(r"\b((?:19|20)\d{2})\b", ed) + if m: + return "year", m.group(1) + return None, None + + +def ordinal(n: str) -> str: + num = int(n) + if 10 <= num % 100 <= 20: + suf = "th" + else: + suf = {1: "st", 2: "nd", 3: "rd"}.get(num % 10, "th") + return f"{num}{suf}" + + +def http_get_json(url: str) -> dict: + req = urllib.request.Request( + url, headers={"User-Agent": UA, "Accept": "application/json"} + ) + with urllib.request.urlopen(req, timeout=30) as resp: + return json.loads(resp.read().decode("utf-8")) + + +def http_get(url: str) -> tuple[str, str]: + req = urllib.request.Request( + url, + headers={ + "User-Agent": ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/124.0.0.0 Safari/537.36" + ), + "Accept-Language": "en", + }, + ) + with urllib.request.urlopen(req, timeout=30) as resp: + charset = resp.headers.get_content_charset() or "utf-8" + return resp.geturl(), resp.read().decode(charset, "replace") + + +def cover_exists(url: str) -> bool: + check = url + ("&" if "?" in url else "?") + "default=false" + req = urllib.request.Request(check, method="HEAD", headers={"User-Agent": UA}) + try: + with urllib.request.urlopen(req, timeout=15) as resp: + return 200 <= resp.status < 400 + except urllib.error.HTTPError as exc: + return exc.code != 404 + except Exception: + return False + + +def title_overlap(a: str, b: str) -> float: + aw, bw = set(normalize(a).split()), set(normalize(b).split()) + if not aw: + return 0.0 + return len(aw & bw) / len(aw) + + +def edition_in_text(text: str, kind: str, value: str) -> bool: + t = normalize(text) + if kind == "nth": + ord_ = ordinal(value) + patterns = [ + rf"\b{re.escape(value)}\s*edition\b", + rf"\b{re.escape(ord_)}\s*edition\b", + rf"\b{re.escape(ord_)}\b", + rf",\s*{re.escape(value)}\b", + ] + return any(re.search(p, t) for p in patterns) + if kind == "year": + return bool(re.search(rf"\b{re.escape(value)}\b", t)) + return False + + +def search_ol(query: str) -> list[dict]: + url = "https://openlibrary.org/search.json?" + urllib.parse.urlencode( + {"q": query, "limit": 20} + ) + try: + return http_get_json(url).get("docs") or [] + except Exception: + return [] + + +def pick_ol_cover(book: dict, kind: str, value: str) -> str: + title = book["title"] + queries = [ + f"{title} {ordinal(value)} edition" if kind == "nth" else f"{title} {value}", + f"{title} {value} edition" if kind == "nth" else title, + title, + ] + # de-dupe + seen_q = set() + docs: list[dict] = [] + seen_keys = set() + for q in queries: + if q in seen_q: + continue + seen_q.add(q) + for doc in search_ol(q): + key = doc.get("key") or str(doc.get("cover_i")) + if key in seen_keys: + continue + seen_keys.add(key) + docs.append(doc) + time.sleep(PAUSE) + + best_url = "" + best_score = -1.0 + for doc in docs: + doc_title = doc.get("title") or "" + overlap = title_overlap(title, doc_title) + if overlap < 0.55: + continue + ed_hit = edition_in_text(doc_title, kind, value) + # Also check subtitle-ish fields + if not ed_hit: + blob = " ".join( + [ + doc_title, + str(doc.get("subtitle") or ""), + " ".join(str(x) for x in (doc.get("isbn") or [])[:1]), + ] + ) + ed_hit = edition_in_text(blob, kind, value) + + score = overlap * 10 + if ed_hit: + score += 25 + else: + # Without an explicit edition signal, skip — avoid wrong editions. + continue + + cover_i = doc.get("cover_i") + url = "" + if cover_i: + url = OL_COVER.format(id=int(cover_i)) + else: + for isbn in doc.get("isbn") or []: + clean = re.sub(r"[^0-9Xx]", "", str(isbn)) + if len(clean) in (10, 13): + candidate = OL_ISBN.format(isbn=clean) + if cover_exists(candidate): + url = candidate + break + if not url: + continue + if score > best_score: + best_score = score + best_url = url + return best_url + + +def pick_goodreads_isbn_cover(book: dict, kind: str, value: str) -> str: + """Fallback: Open Library editions → ISBN → Goodreads og:image.""" + title = book["title"] + q = f"{title} {ordinal(value)} edition" if kind == "nth" else f"{title} {value}" + docs = search_ol(q) or search_ol(title) + time.sleep(PAUSE) + isbns: list[str] = [] + for doc in docs[:5]: + if title_overlap(title, doc.get("title") or "") < 0.5: + continue + for isbn in doc.get("isbn") or []: + clean = re.sub(r"[^0-9Xx]", "", str(isbn)) + if len(clean) in (10, 13): + isbns.append(clean) + key = doc.get("key") + if key and str(key).startswith("/works/"): + try: + editions = http_get_json( + f"https://openlibrary.org{key}/editions.json?limit=15" + ) + except Exception: + editions = {} + for entry in editions.get("entries") or []: + ed_title = entry.get("title") or "" + ed_name = entry.get("edition_name") or "" + blob = f"{ed_title} {ed_name} {entry.get('publish_date') or ''}" + if not edition_in_text(blob, kind, value) and kind == "nth": + # still collect if edition_name matches + if not edition_in_text(ed_name, kind, value): + continue + for field in ("isbn_13", "isbn_10"): + for isbn in entry.get(field) or []: + clean = re.sub(r"[^0-9Xx]", "", str(isbn)) + if len(clean) in (10, 13): + isbns.append(clean) + time.sleep(PAUSE) + + # unique prefer isbn13 + uniq = [] + seen = set() + for isbn in sorted(isbns, key=lambda x: (0 if len(x) == 13 else 1, x)): + if isbn not in seen: + seen.add(isbn) + uniq.append(isbn) + + for isbn in uniq[:6]: + try: + final, page = http_get(f"https://www.goodreads.com/book/isbn/{isbn}") + except Exception: + continue + if "captcha" in final.lower() or "verify" in final.lower(): + continue + m = re.search( + r'property=["\']og:image["\']\s+content=["\']([^"\']+)["\']', + page, + flags=re.I, + ) + if not m: + continue + img = m.group(1).replace("&", "&") + if "goodreads_wide" in img or "facebook/goodreads" in img: + continue + if "books/" in img or "media-amazon.com" in img or "gr-assets" in img: + # Prefer when page text mentions edition + if kind == "nth" and not edition_in_text(page[:8000], kind, value): + # still accept if title overlap on og:title + mt = re.search( + r'property=["\']og:title["\']\s+content=["\']([^"\']+)["\']', + page, + flags=re.I, + ) + if mt and edition_in_text(mt.group(1), kind, value): + return img + if mt and title_overlap(title, mt.group(1)) >= 0.6: + return img + continue + return img + time.sleep(PAUSE) + return "" + + +def pick_manning(book: dict) -> str: + slug = re.sub(r"[^\w\s-]", "", book["title"].lower()) + slug = re.sub(r"\s+", "-", slug.strip()) + # common substitutions + slug = slug.replace("c++", "c-plus-plus").replace("c#", "c-sharp") + try: + final, page = http_get(f"https://www.manning.com/books/{slug}") + except Exception: + return "" + if "/books/" not in final: + return "" + m = re.search(r"https://images\.manning\.com/310/310/crop/book/[^\"']+", page) + if m: + return m.group(0).replace( + "https://images.manning.com/310/310/crop/", + "https://images.manning.com/360/480/resize/", + ) + m = re.search( + r'property=["\']og:image["\']\s+content=["\']([^"\']+)["\']', page, flags=re.I + ) + if m and "twitter.png" not in m.group(1): + return m.group(1) + return "" + + +def lookup(book: dict) -> tuple[str, str]: + edition = book.get("edition") or "" + if SKIP_EDITION.search(edition): + return "", "skip-docs" + kind, value = edition_info(edition) + if not kind or not value: + return "", "skip" + # Years used as edition labels like 2025 Edition — try, but weaker + if kind == "year" and int(value) >= 1900: + pass + + url = pick_ol_cover(book, kind, value) + if url: + return url, "ol" + + url = pick_manning(book) + if url: + return url, "manning" + + url = pick_goodreads_isbn_cover(book, kind, value) + if url: + return url, "goodreads" + + return "", "miss" + + +def main() -> None: + books = load_books() + candidates = [] + for b in books: + ed = b.get("edition") or "" + if SKIP_EDITION.search(ed): + continue + kind, value = edition_info(ed) + if not kind: + continue + # Focus on nth editions + recent year editions + if kind == "year" and not re.search(r"edition", ed, re.I): + continue + candidates.append(b) + + print(f"Candidates: {len(candidates)}") + updates: dict[str, str] = {} + stats = {"ol": 0, "goodreads": 0, "manning": 0, "miss": 0, "skip": 0, "skip-docs": 0, "same": 0} + + def worker(book: dict): + time.sleep(PAUSE) + return book["id"], *lookup(book), book.get("coverImage") or "" + + done = 0 + with ThreadPoolExecutor(max_workers=MAX_WORKERS) as pool: + futures = {pool.submit(worker, b): b for b in candidates} + for fut in as_completed(futures): + book = futures[fut] + try: + bid, url, source, old = fut.result() + except Exception as exc: # noqa: BLE001 + print(f"error {book['id']}: {exc}") + bid, url, source, old = book["id"], "", "miss", "" + stats[source] = stats.get(source, 0) + 1 + if url: + if url == old: + stats["same"] = stats.get("same", 0) + 1 + else: + updates[bid] = url + print( + f"+ [{source}] {book['title'][:48]} ({book.get('edition')}) " + f"\n {old[:70]}\n -> {url[:70]}" + ) + done += 1 + if done % 25 == 0: + print(f"progress {done}/{len(candidates)} updates={len(updates)} {stats}") + # checkpoint + for b in books: + if b["id"] in updates: + b["coverImage"] = updates[b["id"]] + save_books(books) + + for b in books: + if b["id"] in updates: + b["coverImage"] = updates[b["id"]] + save_books(books) + print(f"Done. Updated {len(updates)}. Stats={stats}") + + +if __name__ == "__main__": + main() diff --git a/web/data/books.json b/web/data/books.json index e7c34e5..d2b1aed 100644 --- a/web/data/books.json +++ b/web/data/books.json @@ -27,7 +27,7 @@ "category": "beginner", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/1NbLN0H0_nait6xOildlg0RSehU5PXzDP/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/7363640-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1697875493i/197522411.jpg" }, { "id": "python-introducing-python-modern-computing-in-simple-packages", @@ -67,7 +67,7 @@ "category": "intermediate", "edition": "4th Edition", "driveUrl": "https://drive.google.com/file/d/1Iqq_s71shEtgb5j7Y0g2-7gFU-UMtjiH/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8508566-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/8/7806c05-d584-4f7e-a72b-e11e653a184d/harms.png" }, { "id": "python-effective-python-125-specific-ways-to-write-better-python", @@ -107,7 +107,7 @@ "category": "intermediate", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/18VgBH8O9Vy1q3zCkpssl6VO8RxzwIKzm/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/388536-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/id/8508883-L.jpg" }, { "id": "python-python-distilled", @@ -287,7 +287,7 @@ "category": "specialized", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1A-6FFzKsKH8KBWKe-Xex6x3nKUJRttpn/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8513526-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/isbn/9781718501126-L.jpg" }, { "id": "python-python-for-cybersecurity", @@ -377,7 +377,7 @@ "category": "specialized", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/18M1yiC_W2OBmBD_Ob4VrKaMwr4V1-5J1/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8508634-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1642366393i/60139844.jpg" }, { "id": "python-raspberry-pi-cookbook", @@ -457,7 +457,7 @@ "category": "specialized", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/1qgnbndzOPoViT4eJ7gU9bWkTkF1CjN0O/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/15225524-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/0/554d3c2-99e0-4445-8966-3fc874fe7d75/Raschka-HI.png" }, { "id": "python-llm-engineer-s-handbook", @@ -487,7 +487,7 @@ "category": "specialized", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1_b19XqS6UDqgnKXPlL8Dyeb4LTVqsYYN/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/7481281-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/id/8507552-L.jpg" }, { "id": "python-django-for-professional", @@ -527,7 +527,7 @@ "category": "references", "edition": "5th Edition", "driveUrl": "https://drive.google.com/file/d/1fAiatWg2i2BwHviGybMPUrwafRDBtgEg/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/isbn/9781449357016-L.jpg" }, { "id": "python-python-testing-with-pytest", @@ -537,7 +537,7 @@ "category": "references", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1CanVLEhQ_XOgrq4YRm2fRUkfWx07CqF0/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/8508770-L.jpg" }, { "id": "python-beyond-the-basic-stuff-with-python", @@ -547,7 +547,7 @@ "category": "references", "edition": "First & only edition", "driveUrl": "https://drive.google.com/file/d/1txbFLEtmJrp4H9P7oUC048jwLUPOYHBa/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://nostarch.com/sites/default/files/BeyondBasicPython.jpg" }, { "id": "python-the-python-3-standard-library-by-example", @@ -557,7 +557,7 @@ "category": "references", "edition": "First & only edition", "driveUrl": "https://drive.google.com/file/d/134t_JdiXzJ6vKcEjaprmm8X7uCs7AHjH/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/8508847-L.jpg" }, { "id": "python-learning-python", @@ -567,7 +567,7 @@ "category": "references", "edition": "5th Edition", "driveUrl": "https://drive.google.com/file/d/1rIJ6NyKuTAM9hgGC2HDoMKaxBVIcAWt-/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://images.booksense.com/images/739/355/9781449355739.jpg" }, { "id": "javascript-javascript-from-beginner-to-professional", @@ -587,7 +587,7 @@ "category": "beginner", "edition": "4th Edition", "driveUrl": "https://drive.google.com/file/d/1Z8AS0zomMHxPY1wbcE6vCp7aD_s0oqxq/view?usp=drive_link", - "coverImage": "https://covers.openlibrary.org/b/id/7082166-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1721101202i/216524172.jpg" }, { "id": "javascript-head-first-javascript-programming", @@ -777,7 +777,7 @@ "category": "advanced", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/1RqzHBs-_3vxKkf3A3YCY8PBkv1huE3rO/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8511696-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/a/b85f344-edd7-4833-adf3-d98878088441/resig.png" }, { "id": "javascript-hands-on-javascript-high-performance", @@ -907,7 +907,7 @@ "category": "specialized", "edition": "2019 Edition", "driveUrl": "https://drive.google.com/file/d/1aHgS2r4TaK-hwEMnqUAA5kNA9H2TqELN/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8799590-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/2/8a23d37-c21c-491a-a5a9-498b6b54fe6d/Dabit-React-HI.png" }, { "id": "javascript-javascript-for-data-science", @@ -937,7 +937,7 @@ "category": "specialized", "edition": "2021 Edition", "driveUrl": "https://drive.google.com/file/d/1CFMO7b7y7rQUcEGfJ4lPqV43ics65cfy/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8511714-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/9/fbf3ffb-ff3c-4fcd-8441-dcbb65c6339a/Costa-TJSA-HI.png" }, { "id": "javascript-composing-software", @@ -987,7 +987,7 @@ "category": "specialized", "edition": "1st edition, 2020", "driveUrl": "https://drive.google.com/file/d/1G3w7HDk3qrV4RJikxgQ5msle4eyzl3SL/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8508215-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/4/0408a6a-25d5-4a77-a851-ca502e1fa253/Cai-DLJS-RGB.jpg" }, { "id": "javascript-browser-machine-learning-mastery", @@ -1017,7 +1017,7 @@ "category": "references", "edition": "7th Edition", "driveUrl": "https://drive.google.com/file/d/1ZlTJ_sVZst1Hp0o-TSQUM8DsdkpK7Ieq/view?usp=drive_link", - "coverImage": "/covers/reference.svg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1589824179i/49828186.jpg" }, { "id": "javascript-professional-javascript-for-web-developers", @@ -1027,7 +1027,7 @@ "category": "references", "edition": "5th Edition", "driveUrl": "https://drive.google.com/file/d/1uGvyYs8Q0Pf5t3v1RGnRKawU7ond7E3K/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1568670487i/34428125.jpg" }, { "id": "javascript-javascript-es2015-enlightenment", @@ -1047,7 +1047,7 @@ "category": "references", "edition": "First & only edition", "driveUrl": "https://drive.google.com/file/d/1nYPIMuOO-VmPbGNj_WlxSlYim4jFb42u/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://d2sofvawe08yqg.cloudfront.net/javascriptallongesix/s_hero2x?1620458665" }, { "id": "javascript-javascript-the-first-20-years", @@ -1077,7 +1077,7 @@ "category": "references", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/1TLqPvemKX4PaRs2zA5JN_09Vvb17BPTU/view?usp=drive_link", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/10724321-L.jpg" }, { "id": "java-think-java", @@ -1087,7 +1087,7 @@ "category": "beginner", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/12WxLCFLPzaGS9lqsITIYDtJ6_aJQYT8l/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/15234055-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/isbn/9781492072508-L.jpg" }, { "id": "java-java-23-for-absolute-beginners", @@ -1147,7 +1147,7 @@ "category": "beginner", "edition": "5th Edition", "driveUrl": "https://drive.google.com/file/d/1gwDLRPrdqbTp5mrUUdFWiUJFdjQnBPBa/view?usp=drive_link", - "coverImage": "https://covers.openlibrary.org/b/id/10991044-L.jpg" + "coverImage": "https://images.booksense.com/images/538/145/9781098145538.jpg" }, { "id": "java-head-first-design-patterns", @@ -1157,7 +1157,7 @@ "category": "intermediate", "edition": "2nd Edition - 2020", "driveUrl": "https://drive.google.com/file/d/1nI56g93MpgofVd2YCDMYTVCg0Fv4yA5e/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/388950-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1606659025i/56083609.jpg" }, { "id": "java-effective-java", @@ -1167,7 +1167,7 @@ "category": "intermediate", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/1iTI492QvudYpqeitJfNFGbpv_svNSmk5/view?usp=drive_link", - "coverImage": "https://covers.openlibrary.org/b/id/1176573-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/isbn/9780134685991-L.jpg" }, { "id": "java-java-cookbook", @@ -1227,7 +1227,7 @@ "category": "intermediate", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1pLhjwG9rYLzr3PHwaCREk_Scyrn483-y/view?usp=drive_link", - "coverImage": "https://covers.openlibrary.org/b/id/7505404-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/c/a805f5a-5327-480f-b2e9-8327c55343fd/evans.png" }, { "id": "java-100-java-mistakes-and-how-to-avoid-them", @@ -1377,7 +1377,7 @@ "category": "specialized", "edition": "2022 Edition", "driveUrl": "https://drive.google.com/file/d/15p73C6avjiFS5VwIf2g9a0rKxuzAx9gR/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/13530056-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/e/4bef175-7d20-45d2-a867-b56b6874754e/Musib-SB-HI.png" }, { "id": "java-spring-in-action", @@ -1387,7 +1387,7 @@ "category": "specialized", "edition": "6th Edition", "driveUrl": "https://drive.google.com/file/d/1e-gRL-z2JlTRmSJ4c17mgTUFgurl_cqe/view?usp=drive_link", - "coverImage": "https://covers.openlibrary.org/b/id/8722096-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/f/6f120e1-5d88-413d-a884-b88add43bfca/walls2.png" }, { "id": "java-optimizing-cloud-native-java", @@ -1407,7 +1407,7 @@ "category": "specialized", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/11I4zGm0Fye0cu_Elsg7iCacab_R5JkOx/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/13276048-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/6/e751b54-0e51-4676-b05f-c0f023d152b9/Spilca-Spring-HI.png" }, { "id": "java-designing-data-intensive-applications", @@ -1437,7 +1437,7 @@ "category": "specialized", "edition": "2023 Edition", "driveUrl": "https://drive.google.com/file/d/1b7BBQI2S59yL6ZKNM4zQTHN_cSCLuf0x/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/12550182-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/e/98a1ae4-fb7c-4357-9852-01dde05c6431/Tudose-HI.png" }, { "id": "java-building-modern-web-apps-with-spring-boot-and-vaadin", @@ -1457,7 +1457,7 @@ "category": "specialized", "edition": "1st Edition - 2025", "driveUrl": "https://drive.google.com/file/d/1dSOkdgYGizQoV88kCbSLk9-X1pCu3YDp/view?usp=sharing", - "coverImage": "https://images.manning.com/360/480/resize/book/6/35a97a2-f353-48df-8f9f-bf09d5d3f0be/DOTD_Stefanko.png" + "coverImage": "https://images.manning.com/360/480/resize/book/3/bae9433-1f21-40f9-81ec-07523f0f8c34/Stefanko-HI.png" }, { "id": "java-the-java-language-specification", @@ -1477,7 +1477,7 @@ "category": "specialized", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1qfgjmGruyZre1Z7pt7F5u6ApFXFQgfVU/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8508871-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/3/3c80b2a-7fc0-4774-8a1c-51461dc067a8/Carnell-Spring-HI.png" }, { "id": "java-high-performance-java-persistence", @@ -1497,7 +1497,7 @@ "category": "specialized", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/1Pnv6bQUlfIAK8M2DgfnUlzg2XRHZmesq/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/954564-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/f/735179a-e5b6-40d7-a497-bfe187c92080/massol.png" }, { "id": "java-the-art-of-multiprocessor-programming", @@ -1507,7 +1507,7 @@ "category": "references", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/168IKLmFzRpiaVvRA5lHXvTMXc8g_rYTf/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/2320484-L.jpg" }, { "id": "java-modern-concurrency-in-java-virtual-threads-structured-concurrency-and-beyond", @@ -1557,7 +1557,7 @@ "category": "references", "edition": "13th Edition", "driveUrl": "https://drive.google.com/file/d/1auXGujIKKFyw0WU9xB07LyX4Oz2h_yPb/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://books.google.com/books/content?id=sLnwzwEACAAJ&printsec=frontcover&img=1&zoom=1" }, { "id": "java-java-in-a-nutshell", @@ -1567,7 +1567,7 @@ "category": "references", "edition": "8th Edition", "driveUrl": "https://drive.google.com/file/d/1qIPTpVltTi02m6QdRvynIDtgr0HVE80n/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/805638-L.jpg" }, { "id": "java-java-pocket-guide", @@ -1575,9 +1575,9 @@ "author": "", "language": "java", "category": "references", - "edition": "5th Edition", + "edition": "4th Edition", "driveUrl": "https://drive.google.com/file/d/11F1OMndhAHvHTLXDd2HbU4KvLk2JZtWt/view?usp=drive_link", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/isbn/9781491938690-L.jpg" }, { "id": "csharp-head-first-c", @@ -1667,7 +1667,7 @@ "category": "intermediate", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/14d4FYI3Wcm9_LQ20IO0uCo6ivp89J6aZ/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/193238-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/id/8512410-L.jpg" }, { "id": "csharp-concurrency-in-c-cookbook", @@ -1787,7 +1787,7 @@ "category": "advanced", "edition": "4th Edition", "driveUrl": "https://drive.google.com/file/d/1r-J9sbVlaUI0pSUE7PYB7246ny-AySzg/view?usp=drive_link", - "coverImage": "https://covers.openlibrary.org/b/id/8629217-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1353573426i/16033480.jpg" }, { "id": "csharp-framework-design-guidelines-conventions-idioms-and-patterns-for-reusable-net-lib", @@ -1807,7 +1807,7 @@ "category": "advanced", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/13Nxds-pL6Fg-Lif-Sd-xZwMJrEXDgk0p/view?usp=drive_link", - "coverImage": "https://covers.openlibrary.org/b/id/6386613-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/4/d03ac2e-3009-46cf-9e4c-603819414675/osherove.png" }, { "id": "csharp-adaptive-code-agile-coding-with-design-patterns-and-solid-principles", @@ -1887,7 +1887,7 @@ "category": "specialized", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/1YwwWXEcsUGCNa1U262VqGroRhCaCWUYM/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8950705-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/7/fe7fd13-c11f-4615-90dc-0e6e69e9ef39/hocking.png" }, { "id": "csharp-asp-net-core-in-action", @@ -1907,7 +1907,7 @@ "category": "specialized", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1L04I5D4b6vZJwymZ5jDoSk1XRqaA2var/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8508575-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/2/2cd7852-84a5-44f5-a05b-451d68478e31/Smith-EFC-HI.png" }, { "id": "csharp-microservices-in-net-core", @@ -1917,7 +1917,7 @@ "category": "specialized", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1sGDxI8N22zErgOzhFUGB8t4_cRCF0yEn/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8508579-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/f/562623d-102a-47c4-b12f-4c09af31441e/Horsdal-Microservices-HI.png" }, { "id": "csharp-asp-net-core-architecture-microsoft-docs", @@ -1937,7 +1937,7 @@ "category": "references", "edition": "2023 Edition", "driveUrl": "https://drive.google.com/file/d/13or6QeIIosQcKZGIGoGE7M2Ki0TDeZ5d/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://books.google.com/books/content?id=aTEW0AEACAAJ&printsec=frontcover&img=1&zoom=1" }, { "id": "csharp-functional-programming-in-c", @@ -1947,7 +1947,7 @@ "category": "references", "edition": "6th early release - 2024 Edition", "driveUrl": "https://drive.google.com/file/d/1o-BwSGn2l8tXFiRYOoCGxHGS263pItm3/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://images.manning.com/360/480/resize/book/c/b76a00a-0105-4b3f-9652-a40bb6bf09f9/Buonanno_hires.png" }, { "id": "csharp-programming-c-12", @@ -1957,7 +1957,7 @@ "category": "references", "edition": "2024 Edition", "driveUrl": "https://drive.google.com/file/d/1LQCi_6puixk5lUEG4kJO90N-q0Men6Xa/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://books.google.com/books/publisher/content?id=rUcNEQAAQBAJ&printsec=frontcover&img=1&zoom=1" }, { "id": "csharp-c-12-pocket-reference", @@ -1997,7 +1997,7 @@ "category": "beginner", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1uZglJUCeolG1Gr3qpDKNvhIEnKPC5TlI/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/6684943-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1632338314i/59055389.jpg" }, { "id": "c-c-programming-absolute-beginner-s-guide", @@ -2017,7 +2017,7 @@ "category": "beginner", "edition": "6th Edition", "driveUrl": "https://drive.google.com/file/d/1SFQaL3S0fS1d0xhzXtIP21mRLcIvZtls/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/3879532-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/id/8514543-L.jpg" }, { "id": "c-learn-c-the-hard-way", @@ -2047,7 +2047,7 @@ "category": "intermediate", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/150CoeAbvVpM51AfY2i6F0MSE1gzn1ndQ/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/10183812-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/3/d46f8b9-0b1d-4454-8d8c-f07f9f6e93b5/Gustedt-ModernC-HI.png" }, { "id": "c-low-level-programming", @@ -2107,7 +2107,7 @@ "category": "advanced", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1tpW5GjrvJe1VyXR82DDKhOLkz_lj22sa/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8513615-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1411926431i/22800552.jpg" }, { "id": "c-effective-c-an-introduction-to-professional-c-programming", @@ -2137,7 +2137,7 @@ "category": "specialized", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1gLmOJOSl9lCKpcQIFgipsUcqAJqpncb_/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/135567-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1388284197i/9176515.jpg" }, { "id": "c-embedded-c-coding-standard", @@ -2167,7 +2167,7 @@ "category": "specialized", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/11KXWp5hxwP069hV_R72RczldHHAtG3CE/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/135189-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1388282065i/15927018.jpg" }, { "id": "c-the-linux-programming-interface", @@ -2227,7 +2227,7 @@ "category": "specialized", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/1NxEeBNOV11BI1LuPDaVXPThLsJltfY02/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/410921-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1349023910i/8474434.jpg" }, { "id": "c-the-art-of-debugging-with-gdb-ddd-and-eclipse", @@ -2257,7 +2257,7 @@ "category": "references", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1JNOX-OHCjTq6VP712BKJba3zq2wEsL0b/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/8629281-L.jpg" }, { "id": "c-beej-s-guide-to-c-programming", @@ -2277,7 +2277,7 @@ "category": "beginner", "edition": "5th Edition", "driveUrl": "https://drive.google.com/file/d/12jdDj0WcFLwnSkSGy1y9eAkcF81UwKqg/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/135999-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/id/411054-L.jpg" }, { "id": "cpp-programming-principles-and-practice-using-c", @@ -2467,7 +2467,7 @@ "category": "references", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/19BD-BOKEZNsqXKtozVzA_dGkZMron0Qx/view?usp=drive_link", - "coverImage": "/covers/reference.svg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1347917148i/11103493.jpg" }, { "id": "cpp-c-pocket-reference", @@ -2477,7 +2477,7 @@ "category": "references", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/1aDURg39oCc1zuME_JZL730IoFGNdyKs4/view?usp=drive_link", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/13819416-L.jpg" }, { "id": "typescript-learning-typescript", @@ -2497,7 +2497,7 @@ "category": "beginner", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/11KxULpStEskCTr4FJszGUx9qV1TY4AOw/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/13662791-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/4/04b9267-ee57-4e89-868d-2597fd20d08d/Fain-TSQuickly-HI.png" }, { "id": "typescript-essential-typescript-5", @@ -2687,7 +2687,7 @@ "category": "beginner", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/1BTvPt7YDQF8XgR-YtU8S5cFTppsWVW0_/view?usp=drive_link", - "coverImage": "https://covers.openlibrary.org/b/id/8508174-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/3/ddd56a6-ba2b-4ca4-bda2-540761b91c55/Go-Youngman_hi-res_REV.png" }, { "id": "go-go-in-action", @@ -2697,7 +2697,7 @@ "category": "intermediate", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/1J3ZfepWnOw39BpjWlnOXHU7MPFbPd-zi/view?usp=drive_link", - "coverImage": "https://covers.openlibrary.org/b/id/10678342-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/c/4037d5d-e5e5-49bf-a3c1-480be2907eaa/Kennedy-GO-HI.png" }, { "id": "go-concurrency-in-go", @@ -2737,7 +2737,7 @@ "category": "intermediate", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1dwQ8BZIHzAJsOpmCowBRNhFDju1RFh8O/view?usp=drive_link", - "coverImage": "https://covers.openlibrary.org/b/id/8512680-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/4/cd81ad9-b07a-4f57-8aa2-9b4c8cede836/Butcher-GoinP-HI.png" }, { "id": "go-designing-data-intensive-applications", @@ -2777,7 +2777,7 @@ "category": "advanced", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/1pZCiQxWCQJN7qRfoHmBREQBtwzpesg1R/view?usp=drive_link", - "coverImage": "https://covers.openlibrary.org/b/id/14709754-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/9/5990f3c-19fb-4945-b024-7280e616773f/Harsanyi-HI.png" }, { "id": "go-let-s-go", @@ -2837,7 +2837,7 @@ "category": "references", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/1_VBBGh5PhrSdmYemSOxk7sfOtUOHTeAa/view?usp=drive_link", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/8508549-L.jpg" }, { "id": "rust-the-rust-programming-language", @@ -2897,7 +2897,7 @@ "category": "intermediate", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/17cQzEkMvBFKSIbYYGFizYoQ0S1bZ8l4F/view?usp=drive_link", - "coverImage": "https://covers.openlibrary.org/b/id/11589242-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/6/252ec51-15b6-4236-918f-42fd988aff53/McNamara-Rust-RGB.jpg" }, { "id": "rust-command-line-rust", @@ -2997,7 +2997,7 @@ "category": "specialized", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1HuYUELERFfoNpuyAf6oWxzd3WX_MkSNa/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/13930433-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/9/57fa437-06ef-4a02-8070-bc33e0800c87/Gruber-HI.png" }, { "id": "rust-game-development-with-rust", @@ -3037,7 +3037,7 @@ "category": "references", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/1OHEm3A2ydVj1g6gqwB6mCcJOeJNQGTWW/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://images.manning.com/360/480/resize/book/f/9806549-3756-47ed-89fc-910a6a33f161/Matthews2-HI.png" }, { "id": "rust-effective-rust", @@ -3047,7 +3047,7 @@ "category": "references", "edition": "1st Edition", "driveUrl": "https://www.lurklurk.org/effective-rust/", - "coverImage": "/covers/reference.svg" + "coverImage": "https://books.google.com/books/content?id=WkJa0AEACAAJ&printsec=frontcover&img=1&zoom=1" }, { "id": "php-head-first-php-mysql", @@ -3057,7 +3057,7 @@ "category": "beginner", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/1lQZk9_XSGOL02j07qV8_oS6B_ptCORNz/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/5542007-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1328834323i/3264934.jpg" }, { "id": "php-learning-php-mysql-javascript", @@ -3097,7 +3097,7 @@ "category": "intermediate", "edition": "5th Edition", "driveUrl": "https://drive.google.com/file/d/1yYGg0c5W4Sa4YZU9IKlDOuW-4jMZhoyK/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/411040-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/id/8509492-L.jpg" }, { "id": "php-php-mysql-the-missing-manual", @@ -3277,7 +3277,7 @@ "category": "intermediate", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1NxvVTLNqCmzVPwqpvWTGU3RMIc3US4OM/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8507716-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/3/9458a37-9793-4e67-a23f-585da31dff55/Jemerov-Kotlin-HI.png" }, { "id": "kotlin-programming-kotlin", @@ -3327,7 +3327,7 @@ "category": "advanced", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/1dSp6NJ49inBhTDwQWEC2dtEx7xE7qu2u/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8798807-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/7/2ae7df8-bfc1-4a17-b78f-c40dc4874da7/Saumont-JofK-HI.png" }, { "id": "kotlin-kotlin-coroutines", @@ -3527,7 +3527,7 @@ "category": "intermediate", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1kSS5vaesEfSC_8h-GWKrDioCt3Z7SQiw/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/1098318-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1348432474i/1548279.jpg" }, { "id": "sql-high-performance-mysql", @@ -3587,7 +3587,7 @@ "category": "references", "edition": "4th Edition", "driveUrl": "https://drive.google.com/file/d/1rEXIAP3fyJ4jIoW3HBQL6LhfVWcAiUaS/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/6616859-L.jpg" }, { "id": "sql-modern-sql", @@ -3647,7 +3647,7 @@ "category": "beginner", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/1IWIUDcvNQbI2zNZ_L2lBoEnwGUKpinLT/view?usp=sharing", - "coverImage": "https://images.isbndb.com/covers/568523482821.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1663713217i/62630676.jpg" }, { "id": "swift-ios-development-with-swift", @@ -3667,7 +3667,7 @@ "category": "intermediate", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/1we9nE1pbHEJ9hBSWHNX5tiYObBRyhbjc/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/10105829-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/isbn/9781491987575-L.jpg" }, { "id": "swift-swift-in-depth", @@ -3677,7 +3677,7 @@ "category": "intermediate", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/1Mi4-0eInG74YugNvLj1i6M8Py60uf7TU/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8507819-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/a/ce389e6-8a86-4c0b-82ff-b926071539d9/Veen-Swift-HI.png" }, { "id": "swift-swift-gems", @@ -3777,7 +3777,7 @@ "category": "references", "edition": "Official Documentation", "driveUrl": "https://docs.swift.org/swift-book/documentation/the-swift-programming-language/", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/12641066-L.jpg" }, { "id": "swift-swiftui-views-mastery", @@ -3797,7 +3797,7 @@ "category": "beginner", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/1Cf52TXHVNxnmiormIZxnCAIeTStlqMxN/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/8734237-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/a/235faa9-5cd4-4d62-9199-d32d165339f6/black2.png" }, { "id": "ruby-learn-to-program", @@ -3837,7 +3837,7 @@ "category": "beginner", "edition": "3rd Edition", "driveUrl": "https://drive.google.com/file/d/1a3ZoDJBGjwAbtoZATusPisqY1nz00E_J/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/410686-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1408310899i/16269916.jpg" }, { "id": "ruby-eloquent-ruby", @@ -3907,7 +3907,7 @@ "category": "advanced", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/1bO22y0x2lcdbSzsV-hss7uACKlZxE5pf/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/9986352-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1347345993i/2278064.jpg" }, { "id": "ruby-refactoring-ruby-edition", @@ -3997,7 +3997,7 @@ "category": "references", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/15dMASM-GFoEMMnDm4m_3aJXPjDOsTz1O/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/1312587-L.jpg" }, { "id": "ruby-ruby-cookbook", @@ -4007,7 +4007,7 @@ "category": "references", "edition": "2nd Edition", "driveUrl": "https://drive.google.com/file/d/1m7lBtlRlCdWrG_CIqyaSyRzrYHfZWvtn/view?usp=sharing", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/10361371-L.jpg" }, { "id": "ruby-official-ruby-documentation", @@ -4097,7 +4097,7 @@ "category": "intermediate", "edition": "1st Edition", "driveUrl": "https://drive.google.com/file/d/1SmiglCBKIGbLCuhLXitfApG6TG2Chxbw/view?usp=sharing", - "coverImage": "https://covers.openlibrary.org/b/id/9937155-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/d/9e6b9af-e8b5-40a9-a508-489df174dd33/Windmill-Flutter-HI.png" }, { "id": "dart-dart-apprentice-beyond-the-basics", @@ -4287,7 +4287,7 @@ "category": "intermediate", "edition": "1st Edition", "driveUrl": "https://www.manning.com/books/functional-programming-in-scala", - "coverImage": "https://covers.openlibrary.org/b/id/7866340-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/2/a2ed920-d6ed-48fb-8f18-b051b7a09a2a/bjarnason.png" }, { "id": "scala-scala-cookbook", @@ -4357,7 +4357,7 @@ "category": "specialized", "edition": "2nd Edition", "driveUrl": "https://www.manning.com/books/akka-in-action", - "coverImage": "https://covers.openlibrary.org/b/id/8511699-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/0/446f79b-bc76-4f03-b638-2bbebf02f406/Roestenburg-AkkainA.png" }, { "id": "scala-scala-documentation", @@ -4437,7 +4437,7 @@ "category": "intermediate", "edition": "2nd Edition", "driveUrl": "https://www.manning.com/books/elixir-in-action-second-edition", - "coverImage": "https://covers.openlibrary.org/b/id/14382780-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/5/2e8efb1-9e6f-462c-9487-04eac07ea623/juric.png" }, { "id": "elixir-craft-graphql-apis-in-elixir-with-absinthe", @@ -4567,7 +4567,7 @@ "category": "beginner", "edition": "2nd Edition", "driveUrl": "https://linuxcommand.org/tlcl.php", - "coverImage": "https://covers.openlibrary.org/b/id/7087755-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1544596708i/42353997.jpg" }, { "id": "shell-bash-guide-for-beginners", @@ -4607,7 +4607,7 @@ "category": "references", "edition": "Official Reference", "driveUrl": "https://www.gnu.org/software/bash/manual/", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/1688274-L.jpg" }, { "id": "shell-greg-s-wiki-bashguide", @@ -4727,7 +4727,7 @@ "category": "specialized", "edition": "2nd Edition", "driveUrl": "https://www.statlearning.com/", - "coverImage": "https://covers.openlibrary.org/b/id/8665540-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/isbn/9781071614174-L.jpg" }, { "id": "r-the-elements-of-statistical-learning", @@ -4787,7 +4787,7 @@ "category": "references", "edition": "Official Manual", "driveUrl": "https://cran.r-project.org/doc/manuals/r-release/R-intro.html", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/14630218-L.jpg" }, { "id": "r-the-r-language-definition", @@ -4797,7 +4797,7 @@ "category": "references", "edition": "Official Manual", "driveUrl": "https://cran.r-project.org/doc/manuals/r-release/R-lang.html", - "coverImage": "/covers/reference.svg" + "coverImage": "https://covers.openlibrary.org/b/id/152116-L.jpg" }, { "id": "r-rdocumentation", @@ -5017,7 +5017,7 @@ "category": "intermediate", "edition": "1st Edition", "driveUrl": "https://www.manning.com/books/css-in-depth", - "coverImage": "https://covers.openlibrary.org/b/id/8508986-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/1/05a18b1-751e-4bcd-9d61-71757f0d2d54/Grant-CSS-HI.png" }, { "id": "htmlcss-every-layout", @@ -5037,7 +5037,7 @@ "category": "intermediate", "edition": "2nd Edition", "driveUrl": "https://www.amazon.com/Responsive-Web-Design-Ethan-Marcotte/dp/1937557186", - "coverImage": "https://covers.openlibrary.org/b/id/6715858-L.jpg" + "coverImage": "https://covers.openlibrary.org/b/id/8509197-L.jpg" }, { "id": "htmlcss-css-secrets", @@ -5067,7 +5067,7 @@ "category": "advanced", "edition": "1st Edition", "driveUrl": "https://www.amazon.com/Transcending-CSS-Value-Aesthetic-Design/dp/0321412416", - "coverImage": "https://covers.openlibrary.org/b/id/193974-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1408111238i/258800.jpg" }, { "id": "htmlcss-inclusive-design-patterns", @@ -5267,7 +5267,7 @@ "category": "intermediate", "edition": "3rd Edition", "driveUrl": "https://www.pearson.com/en-us/subject-catalog/p/matlab-guide/P200000003315", - "coverImage": "https://covers.openlibrary.org/b/id/1659605-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1347475629i/7732800.jpg" }, { "id": "matlab-essential-matlab-for-engineers-and-scientists", @@ -5367,7 +5367,7 @@ "category": "beginner", "edition": "3rd Edition", "driveUrl": "https://www.amazon.com/Assembly-Language-Step-Step-Programming/dp/0470497025", - "coverImage": "https://covers.openlibrary.org/b/id/303919-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1264372977i/6616072.jpg" }, { "id": "assembly-x86-64-assembly-language-programming-with-ubuntu", @@ -5387,7 +5387,7 @@ "category": "intermediate", "edition": "2nd Edition", "driveUrl": "https://www.amazon.com/Art-Assembly-Language-2nd/dp/1593272073", - "coverImage": "https://covers.openlibrary.org/b/id/8619355-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1328753489i/6681001.jpg" }, { "id": "assembly-computer-systems-a-programmer-s-perspective", @@ -5407,7 +5407,7 @@ "category": "advanced", "edition": "2nd Edition", "driveUrl": "https://www.amazon.com/Hackers-Delight-2nd-Henry-Warren/dp/0321842685", - "coverImage": "https://covers.openlibrary.org/b/id/136606-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1355057418i/13705622.jpg" }, { "id": "assembly-practical-reverse-engineering", @@ -5477,7 +5477,7 @@ "category": "beginner", "edition": "1st Edition", "driveUrl": "https://www.manning.com/books/get-programming-with-haskell", - "coverImage": "https://covers.openlibrary.org/b/id/8508994-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/d/fc73b0e-44c8-4c24-a20b-0f24b7c8e598/Kurt-Haskell_hi-res.png" }, { "id": "haskell-programming-in-haskell", @@ -5547,7 +5547,7 @@ "category": "specialized", "edition": "1st Edition", "driveUrl": "https://www.manning.com/books/functional-design-and-architecture", - "coverImage": "https://covers.openlibrary.org/b/id/9966329-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/0/a055839-5688-4042-a982-5841ffa6be1d/Granin-HI.png" }, { "id": "haskell-official-haskell-documentation", @@ -5577,7 +5577,7 @@ "category": "beginner", "edition": "4th Edition", "driveUrl": "https://www.manning.com/books/learn-powershell-in-a-month-of-lunches-fourth-edition", - "coverImage": "https://covers.openlibrary.org/b/id/8511906-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/3/54621ee-f7a6-416d-bc9b-eb3391355ef8/jones.png" }, { "id": "powershell-powershell-for-sysadmins", @@ -5607,7 +5607,7 @@ "category": "intermediate", "edition": "2nd Edition", "driveUrl": "https://www.manning.com/books/powershell-in-depth-second-edition", - "coverImage": "https://covers.openlibrary.org/b/id/8513320-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/e/3ec89ca-ffba-40a1-8eda-fa78738ac815/jones2.png" }, { "id": "powershell-learn-powershell-scripting-in-a-month-of-lunches", @@ -5617,7 +5617,7 @@ "category": "intermediate", "edition": "2nd Edition", "driveUrl": "https://www.manning.com/books/learn-powershell-scripting-in-a-month-of-lunches-second-edition", - "coverImage": "https://covers.openlibrary.org/b/id/8851745-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/c/28bf186-9e5b-4cf2-8016-c1752707a313/JonesHicks_Powershell_hires.png" }, { "id": "powershell-the-powershell-scripting-and-toolmaking-book", @@ -5647,7 +5647,7 @@ "category": "specialized", "edition": "2nd Edition", "driveUrl": "https://www.manning.com/books/learn-azure-in-a-month-of-lunches-second-edition", - "coverImage": "https://covers.openlibrary.org/b/id/8507529-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/e/84c2127-3a52-42ae-9010-fc1188a64307/Foulds_Azure_hires.png" }, { "id": "powershell-azure-powershell-quick-start-guide", @@ -5867,7 +5867,7 @@ "category": "specialized", "edition": "1st Edition", "driveUrl": "https://www.manning.com/books/building-ethereum-dapps", - "coverImage": "https://covers.openlibrary.org/b/id/8507547-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/a/0db3419-83dc-49d9-9613-68758dfb27dd/Infante-BED-HI.png" }, { "id": "solidity-ethereum-developer-documentation", @@ -5917,7 +5917,7 @@ "category": "beginner", "edition": "2nd Edition", "driveUrl": "https://www.oreilly.com/library/view/intermediate-perl-2nd/9781449343781/", - "coverImage": "https://covers.openlibrary.org/b/id/8664045-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1328761639i/11242248.jpg" }, { "id": "perl-beginning-perl", @@ -6027,7 +6027,7 @@ "category": "beginner", "edition": "4th Edition", "driveUrl": "https://www.amazon.com/Fortran-Scientists-Engineers-Stephen-Chapman/dp/0073385891", - "coverImage": "https://covers.openlibrary.org/b/id/7391357-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1347228125i/5085679.jpg" }, { "id": "fortran-introduction-to-programming-with-fortran", @@ -6097,7 +6097,7 @@ "category": "specialized", "edition": "1st Edition", "driveUrl": "https://www.amazon.com/Writing-Scientific-Software-Guide-Good/dp/052186862X", - "coverImage": "https://covers.openlibrary.org/b/id/8735984-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1348431088i/2048015.jpg" }, { "id": "fortran-fortran-standards-iso-references", @@ -6127,7 +6127,7 @@ "category": "beginner", "edition": "6th Edition", "driveUrl": "https://www.amazon.com/Programming-Objective-C-6th-Developers-Library/dp/0321967607", - "coverImage": "https://images.isbndb.com/covers/16628183482235.jpg" + "coverImage": "https://covers.openlibrary.org/b/id/8514541-L.jpg" }, { "id": "objectivec-objective-c-programming-the-big-nerd-ranch-guide", @@ -6147,7 +6147,7 @@ "category": "beginner", "edition": "2nd Edition", "driveUrl": "https://www.amazon.com/Learn-Objective-C-Mac-iOS/dp/1430241888", - "coverImage": "https://covers.openlibrary.org/b/id/8686249-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1344734512i/14975238.jpg" }, { "id": "objectivec-cocoa-programming-for-os-x", @@ -6247,7 +6247,7 @@ "category": "beginner", "edition": "2nd Edition", "driveUrl": "https://www.manning.com/books/clojure-in-action", - "coverImage": "https://covers.openlibrary.org/b/id/8511671-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/0/5657eac-1ad1-48b9-a15b-8e7bcf92e4f3/rathore.png" }, { "id": "clojure-living-clojure", @@ -6267,7 +6267,7 @@ "category": "intermediate", "edition": "1st Edition", "driveUrl": "https://www.oreilly.com/library/view/clojure-programming/9781449310387/", - "coverImage": "https://covers.openlibrary.org/b/id/7440852-L.jpg" + "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1344675203i/10883803.jpg" }, { "id": "clojure-the-joy-of-clojure", @@ -6277,7 +6277,7 @@ "category": "intermediate", "edition": "2nd Edition", "driveUrl": "https://www.manning.com/books/the-joy-of-clojure-second-edition", - "coverImage": "https://covers.openlibrary.org/b/id/6718306-L.jpg" + "coverImage": "https://images.manning.com/360/480/resize/book/1/fa46df5-8dda-46df-90f0-d17397f527ae/fogus.png" }, { "id": "clojure-clojure-the-essential-reference", diff --git a/web/lib/covers.ts b/web/lib/covers.ts index d9bb353..f368b62 100644 --- a/web/lib/covers.ts +++ b/web/lib/covers.ts @@ -9,7 +9,7 @@ const REFERENCE_EDITION = const REFERENCE_TITLE = /\b(documentation|\bdocs\b|language reference|style guide|reference manual|\bmdn\b|hexdocs|\bwiki\b)\b/i; -/** Published books that are tagged Online Resource but should keep a real cover. */ +/** Published books tagged Online Resource that should keep a real cover. */ const PUBLISHED_ONLINE_EXCEPTIONS = new Set([ "Scala with Cats", "Zionomicon", @@ -20,14 +20,20 @@ const PUBLISHED_ONLINE_EXCEPTIONS = new Set([ export function isReferenceCoverBook( book: Pick, ): boolean { - if (book.category === "references") return true; if (PUBLISHED_ONLINE_EXCEPTIONS.has(book.title)) return false; + // Real published "references" (Nutshell, Pocket Reference, etc.) keep + // their coverImage when it is a normal http(s) URL. Only force the SVG + // for docs/online resources or when no real cover is set. if (REFERENCE_EDITION.test(book.edition || "")) return true; if (REFERENCE_TITLE.test(book.title || "")) return true; + if (book.category === "references") return true; return false; } export function coverSrcForBook(book: Book): string { + const src = book.coverImage?.trim() || ""; + // Always prefer an explicit remote cover when present. + if (/^https?:\/\//i.test(src)) return src; if (isReferenceCoverBook(book)) return REFERENCE_COVER_SRC; - return book.coverImage?.trim() || ""; + return src; }