A while back there was a small browser extension called My Online Learning Downloader. It did exactly one useful thing: on learning.oreilly.com it added a button that packaged a book into an ePub, so you could read something you already pay for — offline, on a plane, without a tab open. Then the extension was pulled from the store, the author went quiet, and one day the button just… spun forever on “Getting book details and file list…”.
I’m not a developer by trade. But the extension’s source was still sitting in my browser cache, and I had an AI assistant on hand. So I spent an afternoon doing the thing I’d normally never attempt by hand: taking apart a minified extension and putting it back together. This is the story of what was broken and every wall I ran into.
- It’s for books only. The path that downloads an ePub is the one I fixed and tested. The video/caption code inherited from the original is untouched and unverified.
- Personal use only. This is a repair of a dead tool for content you already pay for with your own O’Reilly subscription. Don’t redistribute, re-upload, or resell anything you download. I’m not turning this into a product.
Why it broke
The extension never had a public API to call. It worked by scraping O’Reilly’s page and then hitting O’Reilly’s private web endpoints. Two independent things killed it at once:
- The author’s own servers disappeared. The extension phoned home — an update-ping, an eligibility check, some telemetry, a donation file. With those hosts gone, every call failed silently or retried forever in the background.
- O’Reilly changed the shape of its page data. The extension asked for a book using a value it read out of a JavaScript object on the page. When that object’s structure shifted, the value it grabbed was no longer a valid book ID. The request went to a nonsense URL — and the server didn’t return a 404. It just held the connection open. No error, no timeout, no clue. Just the spinner.
That second one is the whole reason the tool “broke” rather than “errored.” A silent hang is the worst failure mode there is.
What I actually changed
I didn’t rewrite anything. I made the smallest cuts that would let it live:
- Removed the four dead external calls (the phone-home ones and the donation fetch).
- Fixed how the book ID is resolved — instead of trusting one fragile global variable, it now tries three sources and falls back to the ISBN in the page URL, which is the most reliable of all.
- Wrapped every network request in a 30-second timeout with console diagnostics, so a hang now becomes a precise error instead of an eternal spinner.
- Cleaned the manifest (dropped the store update URL, the old signing key, and dead host permissions).
Everything that talks to O’Reilly — the ePub packaging, the API endpoints — I left alone. Less surface area, less to break.
The walls I hit
1. An infinite spinner tells you nothing
The first fix was diagnostic, not functional. When a fetch has no timeout and the server simply never answers, you can’t tell “wrong URL” from “wrong auth” from “service down.” Adding a hard timeout plus one line of logging — the URL it’s calling, the HTTP status, the first few bytes of the response — turned an opaque hang into a readable failure. Half of debugging is refusing to accept silence as an answer.
2. One global variable was never going to be enough
The original code read the book ID from a single spot on the page. That spot is exactly what O’Reilly reorganized. The fix is dull but robust: prefer the obvious field, and if it’s missing, read the 13-digit ISBN straight out of the URL path. The ISBN was in the address bar the entire time — it never moved, while everything around it did.
3. Strict mode does not forgive a dropped const
While reorganizing a couple of lines, a variable declaration got left behind. In a normal script that’s a harmless global; in a strict-mode content script it’s fatal — ReferenceError: r is not defined, thrown before anything downloads. Worse, a sibling line reused a variable that already existed further up the function, so it needed to not be redeclared. Two nearly identical lines, opposite rules. Minified code has no mercy for intuition; you fix it by reading, not guessing.
4. The bug that wasn’t a bug: stale tabs
This one cost me the most time, and it’s the lesson worth sharing. After a fix, the error appeared to persist. The console showed a fresh Extension context invalidated. Here’s what was actually happening: reloading an extension does not re-inject its script into tabs that are already open. Those tabs keep running the old code, disconnected from the reloaded extension — so they reproduce the old error and make a correct fix look broken.
The cure is one keystroke: after reloading the extension, hard-refresh the page (Cmd/Ctrl + Shift + R). To make this obvious, I added a version fingerprint that prints as the very first console line:
[OReillyDL] content script v1.1.40.15 LOADED | /library/view/...
If you don’t see the current version number on that first line, you’re still running the ghost of the old script. Reload, refresh, look again.
5. You can’t just double-click a .crx anymore
Modern Edge and Chrome quietly refuse to install a drag-and-dropped .crx. The practical route is Developer mode → Load unpacked, pointing at the unzipped folder. And if you do want a signed .crx, the packaging key has to be in PKCS#8 format — a small detail that wastes a surprising amount of time when it’s wrong.
6. AI writes faster than you can verify
The meta-lesson: an AI assistant can propose ten edits in the time it takes you to read one. Twice, a patch reported success but never landed, because two edits to the same file raced each other. The speed is real, but so is the need to assert the change actually exists — re-read the file, diff the output, check the version string. “It says it worked” is not the same as “it worked.”
How to install it
- Download and unzip the folder below.
- Open
edge://extensions(orchrome://extensions). - Turn on Developer mode (top-right).
- Click Load unpacked and select the unzipped folder (the one containing
manifest.json). - Log in to O’Reilly, open any book at
/library/view/<slug>/<ISBN>/, click the extension’s button, and the ePub lands in your Downloads folder.
Remember: after any update, reload the extension and hard-refresh the page.
Download — O’Reilly ePub Downloader (personal fork, v1.1.40.15)
Unpacked extension folder + README · ~96 KB · Edge / Chrome
Based on My Online Learning Downloader by My Chrome Extensions, Inc. (MPL-2.0), repaired locally for personal use. Provided as-is, with no warranty. Use only on content you’re entitled to access, and respect O’Reilly’s Terms of Service.


