fix(embeds): stop building broken redd.it / i.redd.it Reddit embeds
embed.reddit.com only renders the /r/<sub>/comments/<id> path (verified against reddit's embed widgets.js + curl: frame-ancestors * , no X-Frame-Options, so iframing itself is fine). But getRedditPostEmbed built a bare embed.reddit.com/comments/<id> for redd.it short links — which serves a 'not found' page — and the i. host strip routed i.redd.it/*.jpg image links into the same branch. Returning a (broken) URL also suppressed renderContent's og:url fallback that would resolve the short link to its canonical /r/<sub>/... form. Fix: getRedditPostEmbed returns null for any non-reddit.com host, so redd.it / i.redd.it / v.redd.it fall through to the og:url fallback (working embed or a normal preview card, never a blank 'not found' iframe). The reddit.com post path is unchanged. Tests updated (22 pass). Note: the live 'broken' symptom is mostly the deploy gap — live still runs the old www.redditmedia.com embed code while the live CSP only allows embed.reddit.com; deploying the current lotus branch (which emits embed.reddit.com) resolves it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -216,15 +216,18 @@ test('Instagram: p / reel / tv → embed path', () => {
|
|||||||
assert.equal(getInstagramEmbed('https://www.instagram.com/someuser/'), null);
|
assert.equal(getInstagramEmbed('https://www.instagram.com/someuser/'), null);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Reddit post embed → redditmedia', () => {
|
test('Reddit post embed → embed.reddit.com', () => {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
getRedditPostEmbed('https://www.reddit.com/r/aww/comments/abc123/cute_cat/'),
|
getRedditPostEmbed('https://www.reddit.com/r/aww/comments/abc123/cute_cat/'),
|
||||||
'https://embed.reddit.com/r/aww/comments/abc123/?ref_source=embed&ref=share&embed=true&theme=dark',
|
'https://embed.reddit.com/r/aww/comments/abc123/?ref_source=embed&ref=share&embed=true&theme=dark',
|
||||||
);
|
);
|
||||||
assert.equal(getRedditPostEmbed('https://old.reddit.com/r/aww/comments/xyz/'), 'https://embed.reddit.com/r/aww/comments/xyz/?ref_source=embed&ref=share&embed=true&theme=dark');
|
assert.equal(getRedditPostEmbed('https://old.reddit.com/r/aww/comments/xyz/'), 'https://embed.reddit.com/r/aww/comments/xyz/?ref_source=embed&ref=share&embed=true&theme=dark');
|
||||||
assert.equal(getRedditPostEmbed('https://www.reddit.com/r/aww/'), null); // subreddit, not a post
|
assert.equal(getRedditPostEmbed('https://www.reddit.com/r/aww/'), null); // subreddit, not a post
|
||||||
// redd.it short link → comments-only embed (no subreddit in the redirect)
|
// redd.it short link / i.redd.it media host can't build the /r/<sub>/comments/<id>
|
||||||
assert.equal(getRedditPostEmbed('https://redd.it/1abc23'), 'https://embed.reddit.com/comments/1abc23/?ref_source=embed&ref=share&embed=true&theme=dark');
|
// path embed.reddit.com requires (a bare /comments/<id> 404s) → null so the caller's
|
||||||
|
// og:url fallback resolves the canonical URL first.
|
||||||
|
assert.equal(getRedditPostEmbed('https://redd.it/1abc23'), null);
|
||||||
|
assert.equal(getRedditPostEmbed('https://i.redd.it/abcd1234.jpg'), null);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('parseMediaEmbed: Instagram/Reddit → rich, Tidal → audio', () => {
|
test('parseMediaEmbed: Instagram/Reddit → rich, Tidal → audio', () => {
|
||||||
|
|||||||
@@ -344,8 +344,9 @@ export function getInstagramEmbed(url: string): string | null {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Reddit (post embed via redditmedia — bypasses the homeserver's blocked
|
// --- Reddit (post embed via embed.reddit.com — reddit's own oEmbed widget host;
|
||||||
// preview fetch, which Reddit serves a bot-check "please wait" page to) ---
|
// www.redditmedia.com now 301s here. Bypasses the homeserver's blocked preview
|
||||||
|
// fetch, which Reddit serves a bot-check "please wait" page to) ---
|
||||||
|
|
||||||
const REDDIT_EMBED_QS = '?ref_source=embed&ref=share&embed=true&theme=dark';
|
const REDDIT_EMBED_QS = '?ref_source=embed&ref=share&embed=true&theme=dark';
|
||||||
|
|
||||||
@@ -353,15 +354,14 @@ export function getRedditPostEmbed(url: string): string | null {
|
|||||||
try {
|
try {
|
||||||
const u = new URL(url);
|
const u = new URL(url);
|
||||||
const h = u.hostname.replace(/^(www|old|new|np|i)\./, '');
|
const h = u.hostname.replace(/^(www|old|new|np|i)\./, '');
|
||||||
// redd.it/<id> short link → reddit.com/comments/<id> (no subreddit needed).
|
// embed.reddit.com ONLY renders the /r/<sub>/comments/<id> path (verified against
|
||||||
if (h === 'redd.it') {
|
// reddit's own embed widgets.js). A bare /comments/<id> — all we could build from a
|
||||||
const id = u.pathname.replace(/^\/+|\/+$/g, '').split('/')[0];
|
// redd.it short link or an i./v.redd.it media host — serves a "not found" page, so
|
||||||
return id ? `https://embed.reddit.com/comments/${id}/${REDDIT_EMBED_QS}` : null;
|
// return null for those and let the caller's og:url fallback resolve the canonical
|
||||||
}
|
// /r/<sub>/comments/<id> URL (from the redirect) before re-parsing into a real embed.
|
||||||
if (h !== 'reddit.com') return null;
|
if (h !== 'reddit.com') return null;
|
||||||
const m = u.pathname.match(/^\/r\/([A-Za-z0-9_]+)\/comments\/([A-Za-z0-9]+)/);
|
const m = u.pathname.match(/^\/r\/([A-Za-z0-9_]+)\/comments\/([A-Za-z0-9]+)/);
|
||||||
if (!m) return null;
|
if (!m) return null;
|
||||||
// embed.reddit.com is the current host (www.redditmedia.com now 301s here).
|
|
||||||
return `https://embed.reddit.com/r/${m[1]}/comments/${m[2]}/${REDDIT_EMBED_QS}`;
|
return `https://embed.reddit.com/r/${m[1]}/comments/${m[2]}/${REDDIT_EMBED_QS}`;
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user