I Built a New Yorker Audio Downloader Extension with AI — Because the “Listen” Button Has No Download

The New Yorker puts a lot of care into its narrated audio. Nearly every feature article ships with a “Listen” player that reads the piece aloud — perfect for a commute, a walk, or washing the dishes. But the player has no download button. If you want to keep an episode for offline listening — a long flight, a dead-zone subway stretch, an old MP3 player with no browser — there is no official way to do it.

I’m not a developer by trade. But I had an AI assistant on hand, a quiet afternoon, and one question that wouldn’t let go: where does this audio actually live? This is the write-up — how we found the URL hiding in the page, why it was downloadable at all, and the small Manifest V3 extension we ended up with.

Before you read on — a few honest words.

  • This is a technical write-up, shared as-is. The extension only points at a media URL that the article page already loads in your own browser. It does not crack anything, bypass any login, or touch any encryption.
  • Personal use only. Use it on articles you already have legitimate access to. Do not redistribute, re-upload, or resell the audio. Respect The New Yorker’s Terms of Service and the copyright on the work.
  • If you enjoy the magazine, the cheapest legitimate path is real: The New Yorker regularly offers a subscription at about $1 per week. That is the deal I’d point anyone to. This tool is a convenience for listening to what you already read — not a substitute for subscribing.

That last point matters to me: the whole reason the audio exists is the journalism. Pay for it if you can.

Why the audio was downloadable at all — the “aha”

I started the way you’d expect: open an article’s source, grep for .mp3 and .m4a. Nothing. The audio isn’t a static file link in the HTML; it’s handed to the player from a JSON blob the page carries. Searching the source for audio turned up a relatedAudio object:

"relatedAudio": {
  "name": "The New Age of Reading",
  "embedUrl": "https:\u002F\u002Fembed-audio.cnevids.com\u002Fpermalink\u002Fnarrated\u002F6ace2d7e2ff8076a7e442422",
  "durationInMs": 4083000
}

Two things stood out:

  • The audio is hosted by Condé Nast’s cnevids platform (embed-audio.cnevids.com).
  • The player’s embedUrl is a “permalink” — a permanent-link-style address.

On a hunch, I just requested that URL directly. It returned the MP3 file itself: ~81 MB, 160 kbps, 48 kHz, ~68 minutes — matching durationInMs exactly. No token, no cookie, no auth handshake. The paywall guards the article text; the narrated audio is loaded by the page’s own player as a plain, public media file. So “download it” is really just “ask for the same URL the player asks for.” Nothing is being broken.

The takeaway that’s worth more than this one tool: a lot of web audio and video players work this way. The address you hand to the player is frequently the real address of the media. Find it, and the rest is plumbing.

What I actually built — a tiny Manifest V3 extension

Since the URL is just sitting there, the extension’s whole job is: open any New Yorker article → find the audio URL → one click → save the MP3. Manifest V3, so it runs on both Chrome and Edge. Three small pieces:

1) content.js — find the URL + draw a button

New Yorker articles are server-rendered, so the relatedAudio JSON is already in the DOM. The content script grabs document.documentElement.outerHTML and runs one regex that tolerates two shapes of the URL: the normal https://embed-audio… and the \u002F-escaped form from the raw source.

const EMBED_RE = /https?:(?:\\u002F|\/)+embed-audio\.cnevids\.com(?:\/|\\u002F)+permalink(?:\/|\\u002F)+narrated(?:\/|\\u002F)+[a-f0-9]+/i;
function normalize(url){ return url.replace(/\\u002F/g,"/").replace(/\\\//g,"/"); }
const m = document.documentElement.outerHTML.match(EMBED_RE);
const embedUrl = m ? normalize(m[0]) : null;

If it finds one, it injects a floating button (bottom-right of the page) and a small toast for feedback, then sends the URL and a cleaned-up article title to the background script.

2) background.js — let the browser do the downloading

The download itself runs in the service worker, using chrome.downloads.download. Deliberately, the extension does not fetch the file itself — it hands the URL to the browser’s own download channel. That means the request goes out through the browser’s network stack (system proxy, cookies, the lot), so there’s no CORS problem and no proxy code to write. It also sanitizes the filename and drops the file into a New Yorker Audio/ subfolder:

const filename = "New Yorker Audio/" + safeName(msg.title) + ".mp3";
chrome.downloads.download({ url: msg.url, filename, conflictAction: "uniquify" }, (id) => {
  if (chrome.runtime.lastError) sendResponse({ ok:false, error: chrome.runtime.lastError.message });
  else sendResponse({ ok:true, id });
});

3) popup.html / popup.js — the toolbar panel

Click the toolbar icon on an article page and the popup asks the content script what it found, then offers a download button. Same URL, same flow — just a second entry point for people who prefer the toolbar to a floating button.

(The three PNG icons are generated with a few lines of Pillow — a play triangle over a download arrow on a dark navy square. Nothing fancy.)

The walls I hit

1) The escaped URL that wasn’t a URL

The raw source writes the address as https:\u002F\u002Fembed-audio…. Feed that string straight to a downloader and it fails — \u002F is a literal six-character escape, not a slash. The fix is one line: normalize \u002F (and the \/ JSON form) back to / before using the URL. I caught it at the regex stage, so it never became a runtime bug.

2) The network was lying to me (locally)

This one is environment-specific and worth a note if you reproduce it: from my machine, www.newyorker.com timed out on the command line (HTTP 000), yet opened fine in the browser. Reason: the browser was using my system proxy (Clash, on 127.0.0.1:7890); curl wasn’t. The fix for local poking is just -x http://127.0.0.1:7890. The fun part: the audio CDN embed-audio.cnevids.com was reachable directly, no proxy needed. None of this matters inside the extension, because we delegate to chrome.downloads and let the browser handle networking — but it’s exactly the kind of ghost that eats an afternoon if you’re debugging the wrong layer.

How to install it

  1. Download and unzip the folder below.
  2. Open edge://extensions (or chrome://extensions).
  3. Turn on Developer mode (top-right).
  4. Click Load unpacked and select the unzipped folder — the one that contains manifest.json.
  5. Open any New Yorker article that has a “Listen” player. A floating button appears at the bottom-right; click it, and the MP3 lands in Downloads/New Yorker Audio/.

Note: modern Chrome and Edge won’t install a drag-dropped .crx file — Load unpacked is the reliable path.

Download — New Yorker Audio Downloader (v1.0.0)

Unpacked extension folder + README · ~9 KB · Edge / Chrome

Download the .zip

Provided as-is, with no warranty. Use only on content you’re entitled to access, and respect The New Yorker’s Terms of Service and the copyright on the work. If you like the magazine, the legitimate subscription is about $1/week — that’s the deal worth taking.

Bluesky 讨论

在 Bluesky 上查看

暂无回复。去 Bluesky 上抢先评论吧!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部