Show the pictures inside Reddit comments
CI / Typecheck, test, build (pull_request) Successful in 11s
CI / Typecheck, test, build (pull_request) Successful in 11s
A comment that was a picture rendered as either a link or, for a Giphy, the literal text ``. On r/aww that is most of the thread. Reddit writes an inline image as a token rather than an address, in three shapes: `` for a Giphy, `` for a subreddit emote, and `` for an image uploaded straight to the comment. The useful part is that all three tokens are keys in that same comment's own `media_metadata`, so this is one lookup and not three special cases. Nothing in the adapter has to know what Giphy is. The fourth shape is someone pasting the address of a picture, which on Reddit is how most images in comments actually arrive -- 117 of them against 21 Giphys in the sample I scanned. Those are shown as pictures too, decided by the file extension. A link that is not to an image stays a link. Animated ones take `s.gif` over `s.mp4` even though the MP4 is several times smaller: a GIF moves on its own in an `<img>`, and an MP4 would need a player element with autoplay, loop and muted set, for something the size of a postage stamp. Everything goes through the `/m/` proxy, like all other media. Without that a comment thread would have the reader's browser fetch dozens of files straight from Reddit, which is the one thing this whole app exists to avoid. Placing the image is the renderer's job, not the Markdown parser's, because the proxy is a render-time concern and markdown.ts knows nothing about it -- so it takes an optional `ImageRenderer` and, without one, an image stays a link exactly as before. The parser also learned `![...]` proper: the link rule was matching from the `[` and stranding the `!` as text. Verified on r/aww/comments/171dxph, which carries one Giphy and 77 pasted images: 35 render on the first page, all 35 load, all 35 through the proxy, no upstream address reaches the page, no token is left unresolved, and nothing overflows the column or scrolls the page sideways. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_017nMQ2eDKnqALYhAibpTKTu
This commit is contained in:
+80
-1
@@ -1,6 +1,6 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { test } from 'node:test';
|
||||
import { commentsFrom, mediaFromLink, toPost, treeFromDepths } from '../src/platforms/reddit.ts';
|
||||
import { commentsFrom, mediaFromLink, resolveInlineImages, toPost, treeFromDepths } from '../src/platforms/reddit.ts';
|
||||
import { reddit } from '../src/platforms/reddit.ts';
|
||||
import { originalUrlFor } from '../src/platforms/index.ts';
|
||||
import { fixture } from './helpers.ts';
|
||||
@@ -168,3 +168,82 @@ test('the page fallback rebuilds nesting from the depth on each comment', () =>
|
||||
test('a comment the page gave no text for is dropped rather than shown empty', () => {
|
||||
assert.deepEqual(treeFromDepths([{ depth: 0, author: 'a', score: 1, created: '', text: '' }]), []);
|
||||
});
|
||||
|
||||
// Real shapes, captured from comments carrying each kind.
|
||||
const GIPHY = {
|
||||
'giphy|Ve7wX45gaOFmw8eeEM': {
|
||||
status: 'valid',
|
||||
e: 'AnimatedImage',
|
||||
m: 'image/gif',
|
||||
s: {
|
||||
y: 200,
|
||||
x: 304,
|
||||
gif: 'https://external-preview.redd.it/CTp8.gif?width=304&height=200&s=b0e9',
|
||||
mp4: 'https://external-preview.redd.it/CTp8.gif?width=304&height=200&format=mp4&s=a389',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const UPLOAD = {
|
||||
jo8gf0ca92zd1: {
|
||||
status: 'valid',
|
||||
e: 'Image',
|
||||
m: 'image/jpeg',
|
||||
s: { y: 1270, x: 1274, u: 'https://preview.redd.it/jo8gf0ca92zd1.jpeg?width=1274&s=c226' },
|
||||
},
|
||||
};
|
||||
|
||||
test('a giphy comment points at the gif rather than at a token', () => {
|
||||
// `` is not an address, and renders as nothing at all until
|
||||
// it is looked up in the comment's own media_metadata.
|
||||
assert.equal(
|
||||
resolveInlineImages('', GIPHY),
|
||||
'',
|
||||
);
|
||||
});
|
||||
|
||||
test('the animated form takes the gif, which plays on its own', () => {
|
||||
const out = resolveInlineImages('', GIPHY);
|
||||
assert.ok(out.includes('.gif?'), out);
|
||||
assert.ok(!out.includes('format=mp4'), 'an mp4 would need a player to move');
|
||||
});
|
||||
|
||||
test('an uploaded image resolves through the same lookup', () => {
|
||||
// Giphy, emotes and uploads are all a token that is a key in the same map,
|
||||
// so none of them needs naming.
|
||||
assert.equal(
|
||||
resolveInlineImages('', UPLOAD),
|
||||
'',
|
||||
);
|
||||
});
|
||||
|
||||
test('a target that is already an address is left alone', () => {
|
||||
const already = '';
|
||||
assert.equal(resolveInlineImages(already, UPLOAD), already);
|
||||
assert.equal(resolveInlineImages(already, undefined), already);
|
||||
});
|
||||
|
||||
test('a token with no entry, or a broken one, is not invented', () => {
|
||||
assert.equal(resolveInlineImages('', GIPHY), '');
|
||||
assert.equal(
|
||||
resolveInlineImages('', { gone: { status: 'failed', e: 'Image' } }),
|
||||
'',
|
||||
);
|
||||
});
|
||||
|
||||
test('inline images survive the walk into the comment tree', () => {
|
||||
const { comments } = commentsFrom({
|
||||
data: {
|
||||
children: [{
|
||||
kind: 't1',
|
||||
data: {
|
||||
author: 'a',
|
||||
body: 'ha ',
|
||||
media_metadata: GIPHY,
|
||||
replies: '',
|
||||
},
|
||||
}],
|
||||
},
|
||||
});
|
||||
assert.match(comments[0]?.text ?? '', /external-preview\.redd\.it/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user