Files
antisocial/test/fixtures/reddit/gallery.json
thatguygriffandClaude Opus 5 6325f0ff32
CI / Typecheck, test, build (pull_request) Successful in 9s
Add Reddit with its comment threads, and show quoted posts whole
Two changes. They share the `Segment` model, which is why they arrive
together.

## Reddit

A new adapter under /reddit, plus the thread beneath the post -- on Reddit the
conversation is usually the reason the link was shared, so a viewer that showed
only the post would be showing the wrong half.

The `.json` twin of a post URL is the post and the whole first page of comments
in one response, far better than anything the page gives up, so it is the only
layer that normally runs. Reddit refuses it to a browser it has never seen and
answers with a JavaScript challenge, which any ordinary navigation solves by
itself; the adapter navigates once and retries, and the cookie left behind
serves every later post. Below that, `shreddit-comment` elements are read from
the rendered page -- flat, each carrying its own `depth`, so `treeFromDepths`
rebuilds the nesting.

`Post.comments` is a tree rather than a flat list with depths, because folding
a comment has to take everything under it along and nesting is what makes that
free. Each comment renders as a `<details open>`, so collapsing works with the
stylesheet off and from the keyboard, and a collapsed one says how many replies
it is hiding. "Collapse all" is the only part that needs the script, so it
ships hidden and appears once the script has run. What sits behind a "load
more" is not fetched -- that is a second page and often a third -- but it is
counted and said out loud rather than quietly dropped.

Four things the payloads got wrong on the first try, each now with a fixture:
`fallback_url` is the video track alone whenever `has_audio` is true, so a post
with sound has to use `hls_url` and only a silent one gets the proxied MP4;
`scrubber_media_url` looks like a poster and is a second MP4 for the timeline
thumbnails, while the still is in `preview.images`; a gallery's pictures live
in `media_metadata` keyed and unordered, with their order only in
`gallery_data`; and `replies` is the string "" rather than an object when there
are none. Comment bodies are Markdown, rendered by a new render/markdown.ts
that escapes first and then puts back only the constructs we chose to support
-- never Reddit's own `body_html`, which would mean trusting markup a stranger
caused to be generated.

An app share link (/r/<sub>/s/<code>) is a plain 301, so one request told not
to follow it is enough. The permalink it resolves to is what the copy button
hands back, since an opaque share code is a tracking parameter by another name.

## Quoted posts

Both X and Bluesky lifted the quoted post's media out and showed it as the
quoter's own, dropping the quoted words and the quoted author entirely. A quote
of a photo post therefore rendered as somebody else's picture under the wrong
name with nothing to say so, and a quote that had a picture of its own dropped
the quoted one instead -- the two could never both appear. Half the quote posts
people share are someone answering a stranger and the other half are someone
continuing a thought from an earlier post; neither reads with only one side of
it on the page.

`Segment.quoted` now carries the whole thing -- author, words, pictures, time
and a link to it -- and renders as a post inside the post. Neither payload
carries a usable address for it: X has no permalink and it is rebuilt from the
handle and `id_str`, Bluesky has an `at://` URI nobody can open and it is
rebuilt from the handle and the record key. On Bluesky the record sits at
`embed.record` for a plain quote and at `embed.record.record` when the quoting
post has media of its own, and a quote can also point at a feed, a list or a
post since deleted, which arrive in the same slot under a different `$type` --
only `app.bsky.embed.record#viewRecord` is taken.

Two things about X's text, both visible on any post and not only a quote. It
arrives pre-escaped, so an ampersand someone typed was reaching the page as the
literal `&amp;`; it is decoded in the adapter, where the encoding comes from,
leaving the escape-on-the-way-out rule alone. And every link is a `t.co`, which
tells the reader nothing and routes them through X's click tracker to find out
-- `entities.urls` carries the real address alongside, so it is put back. The
shortlink X staples onto the end of a quote post is dropped rather than
expanded, since the post it points at is already on the page;
`display_text_range` is where that boundary is and it keeps a link the author
put there deliberately. Its indices are UTF-16 units into the escaped text, so
the slice happens before decoding and before expanding, and splitting to
codepoints first overshoots past an emoji -- checked against a post carrying
one.

A quote is context, and context that fills the screen has stopped being
context, so a segment carrying one gives up the window-filling cap on its own
media.

## Along the way

setupMedia only ever wired `document.querySelector('.media')`, the first rail
on the page. That was already wrong for a Bluesky or Threads chain with media
in more than one post, and a quoted carousel would have hit it too. It now runs
per rail.

## Verified

108 tests, typecheck and build clean. Resolved end to end against the live
platforms: Reddit self, gallery, video, link and share-link posts; X quotes
with no media, with media on the quoted side, and with media on both; Bluesky
quotes in both embed shapes, including a ten-post chain where every post quotes
a different account and all nine quotes come back under the right name.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017nMQ2eDKnqALYhAibpTKTu
2026-08-27 11:45:40 -03:00

542 lines
20 KiB
JSON
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[
{
"kind": "Listing",
"data": {
"children": [
{
"kind": "t3",
"data": {
"title": "Spay day kinda nervy",
"author": "Background_Round_853",
"subreddit_name_prefixed": "r/aww",
"created_utc": 1786959785,
"selftext": "",
"permalink": "/r/aww/comments/1vqnp2e/spay_day_kinda_nervy/",
"url": "https://www.reddit.com/gallery/1vqnp2e",
"url_overridden_by_dest": "https://www.reddit.com/gallery/1vqnp2e",
"domain": "reddit.com",
"is_self": false,
"is_video": false,
"is_gallery": true,
"over_18": false,
"spoiler": false,
"num_comments": 443,
"score": 40774,
"gallery_data": {
"items": [
{
"is_deleted": false,
"media_id": "6xfm6s4wowjh1",
"id": 1020169008
},
{
"is_deleted": false,
"media_id": "x1o3cr4wowjh1",
"id": 1020169009
},
{
"is_deleted": false,
"media_id": "kury9t4wowjh1",
"id": 1020169010
},
{
"is_deleted": false,
"media_id": "v07ubs4wowjh1",
"id": 1020169011
}
]
},
"media_metadata": {
"v07ubs4wowjh1": {
"status": "valid",
"e": "Image",
"m": "image/jpg",
"p": [
{
"y": 155,
"x": 108,
"u": "https://preview.redd.it/v07ubs4wowjh1.jpg?width=108&crop=smart&auto=webp&s=aa2a1ab3c453178f9ba6b8a06800c0fa923ad570"
},
{
"y": 311,
"x": 216,
"u": "https://preview.redd.it/v07ubs4wowjh1.jpg?width=216&crop=smart&auto=webp&s=ec1e4d46b41511ffb3c27e2e331d23469c6be827"
},
{
"y": 461,
"x": 320,
"u": "https://preview.redd.it/v07ubs4wowjh1.jpg?width=320&crop=smart&auto=webp&s=22812e3829549a461bdbcb81232a8edf752cac10"
},
{
"y": 922,
"x": 640,
"u": "https://preview.redd.it/v07ubs4wowjh1.jpg?width=640&crop=smart&auto=webp&s=56315dc4cf19d12e6f19401792a059f70314ef04"
},
{
"y": 1383,
"x": 960,
"u": "https://preview.redd.it/v07ubs4wowjh1.jpg?width=960&crop=smart&auto=webp&s=b706a7d2164462251f869651679c3635e15b6d25"
},
{
"y": 1555,
"x": 1080,
"u": "https://preview.redd.it/v07ubs4wowjh1.jpg?width=1080&crop=smart&auto=webp&s=e5af0cc235a7b2583d8e8a87f7799607e41f1f23"
}
],
"s": {
"y": 1736,
"x": 1205,
"u": "https://preview.redd.it/v07ubs4wowjh1.jpg?width=1205&format=pjpg&auto=webp&s=d5f2f69da610bf9a9b5b7cb99f94c21d21d4e75e"
},
"id": "v07ubs4wowjh1"
},
"6xfm6s4wowjh1": {
"status": "valid",
"e": "Image",
"m": "image/jpg",
"p": [
{
"y": 140,
"x": 108,
"u": "https://preview.redd.it/6xfm6s4wowjh1.jpg?width=108&crop=smart&auto=webp&s=231c0253d9ffcadcfa085c55264f968e1be7cd38"
},
{
"y": 281,
"x": 216,
"u": "https://preview.redd.it/6xfm6s4wowjh1.jpg?width=216&crop=smart&auto=webp&s=efb67b41fa2afbde45b4fbf3ff389cb7c98cbe62"
},
{
"y": 417,
"x": 320,
"u": "https://preview.redd.it/6xfm6s4wowjh1.jpg?width=320&crop=smart&auto=webp&s=4709db023af013805c8a0e4d64fdde867900df2c"
},
{
"y": 834,
"x": 640,
"u": "https://preview.redd.it/6xfm6s4wowjh1.jpg?width=640&crop=smart&auto=webp&s=2a43ebc507dc45cf578f7e74b62b6d37861d4f18"
},
{
"y": 1252,
"x": 960,
"u": "https://preview.redd.it/6xfm6s4wowjh1.jpg?width=960&crop=smart&auto=webp&s=f8f82d7392e30f1fb0d449489972ee2cd2e77d1f"
},
{
"y": 1408,
"x": 1080,
"u": "https://preview.redd.it/6xfm6s4wowjh1.jpg?width=1080&crop=smart&auto=webp&s=d438c9c25573da6b99ec89d26b373cf676d0a727"
}
],
"s": {
"y": 1572,
"x": 1205,
"u": "https://preview.redd.it/6xfm6s4wowjh1.jpg?width=1205&format=pjpg&auto=webp&s=ce8cff5c5c1d347a25c3c4431290fa76637f85b1"
},
"id": "6xfm6s4wowjh1"
},
"kury9t4wowjh1": {
"status": "valid",
"e": "Image",
"m": "image/jpg",
"p": [
{
"y": 141,
"x": 108,
"u": "https://preview.redd.it/kury9t4wowjh1.jpg?width=108&crop=smart&auto=webp&s=cca8d53fa39835bc14cfcb0b70f2409d2c0b41fd"
},
{
"y": 283,
"x": 216,
"u": "https://preview.redd.it/kury9t4wowjh1.jpg?width=216&crop=smart&auto=webp&s=5c196d8c1c5395b4388548729d779a74d011a6a7"
},
{
"y": 420,
"x": 320,
"u": "https://preview.redd.it/kury9t4wowjh1.jpg?width=320&crop=smart&auto=webp&s=6760b50b63edbf681e13739bbccc17551dbcacba"
},
{
"y": 840,
"x": 640,
"u": "https://preview.redd.it/kury9t4wowjh1.jpg?width=640&crop=smart&auto=webp&s=4cf3bb5bd6c53e6d9a8fff6560258da7fb9de4e1"
},
{
"y": 1261,
"x": 960,
"u": "https://preview.redd.it/kury9t4wowjh1.jpg?width=960&crop=smart&auto=webp&s=7c214c8d1a7a951b891b08d43cf445c28cb6ac20"
},
{
"y": 1418,
"x": 1080,
"u": "https://preview.redd.it/kury9t4wowjh1.jpg?width=1080&crop=smart&auto=webp&s=4eca14b69a68d05f78a5c6319867b4102fa3abfb"
}
],
"s": {
"y": 1583,
"x": 1205,
"u": "https://preview.redd.it/kury9t4wowjh1.jpg?width=1205&format=pjpg&auto=webp&s=24c97654ddd1d3bee61788a965223a9e40d92d6e"
},
"id": "kury9t4wowjh1"
},
"x1o3cr4wowjh1": {
"status": "valid",
"e": "Image",
"m": "image/jpg",
"p": [
{
"y": 80,
"x": 108,
"u": "https://preview.redd.it/x1o3cr4wowjh1.jpg?width=108&crop=smart&auto=webp&s=8a3211796cdf827ccdc39d76ff9aab299c4cbe2f"
},
{
"y": 160,
"x": 216,
"u": "https://preview.redd.it/x1o3cr4wowjh1.jpg?width=216&crop=smart&auto=webp&s=52c19a8c35965fccd08e4aaa7bb22f9aae22549e"
},
{
"y": 237,
"x": 320,
"u": "https://preview.redd.it/x1o3cr4wowjh1.jpg?width=320&crop=smart&auto=webp&s=c54e14e1989b0c1a67708fda3fe611da15f52414"
},
{
"y": 474,
"x": 640,
"u": "https://preview.redd.it/x1o3cr4wowjh1.jpg?width=640&crop=smart&auto=webp&s=4b7d4b60bb388c485024efe867466f6e5aba6fad"
},
{
"y": 711,
"x": 960,
"u": "https://preview.redd.it/x1o3cr4wowjh1.jpg?width=960&crop=smart&auto=webp&s=b0e46c04be74d31c5b00c0a704638888a4ca6781"
},
{
"y": 800,
"x": 1080,
"u": "https://preview.redd.it/x1o3cr4wowjh1.jpg?width=1080&crop=smart&auto=webp&s=c9bb5e091d769c8ec1360a83b6164117406641c8"
}
],
"s": {
"y": 893,
"x": 1205,
"u": "https://preview.redd.it/x1o3cr4wowjh1.jpg?width=1205&format=pjpg&auto=webp&s=20fbd865e429feb527bddb0b7f4bed3fc3281075"
},
"id": "x1o3cr4wowjh1"
}
}
}
}
]
}
},
{
"kind": "Listing",
"data": {
"children": [
{
"kind": "t1",
"data": {
"author": "Background_Round_853",
"body": "https://preview.redd.it/d1m4kf45pwjh1.jpeg?width=1206&format=pjpg&auto=webp&s=6ccd095185d203c6569514e4b0bf93ed9de3ed2c\n\nDone ✅✅ she looks DEVASTATED",
"created_utc": 1786959888,
"score": 6583,
"score_hidden": false,
"is_submitter": true,
"replies": {
"kind": "Listing",
"data": {
"children": [
{
"kind": "t1",
"data": {
"author": "Fritzkreig",
"body": "Ah, may your lady and you share mutual joy for the years to come!",
"created_utc": 1786960145,
"score": 943,
"score_hidden": false,
"is_submitter": false,
"replies": ""
}
},
{
"kind": "t1",
"data": {
"author": "malk600",
"body": "She's coming off anaesthesia, idk what the SOC is in your country, but kitteh would typically get dexmedetomidine + opioid + something like ketamine. Vets will typically revert dexmedetomidine w/ atipamezol, which is about as mild and pleasant as being woken up by a swift kick in the nuts. The cat is just tired, confused, restless (it's possible she's going to be woozy and tired as hell but won't be able to sleep) and overall feels like a person would feel after doing a heavy night of things that are supposed to stay in Vegas.\n\nTake good care of her!",
"created_utc": 1786961915,
"score": 1298,
"score_hidden": false,
"is_submitter": false,
"replies": {
"kind": "Listing",
"data": {
"children": [
{
"kind": "t1",
"data": {
"author": "teadazed",
"body": "Yes, I showed up to collect my two (brother and sister cats) and commented on their weirdass pupils. The nurse said fondly \"yeah... that's from all the drugs we've given them\".\nThey were both batting invisible butterflies the rest of the evening.",
"created_utc": 1786966670,
"score": 449,
"score_hidden": false,
"is_submitter": false,
"replies": {
"kind": "Listing",
"data": {
"children": [
{
"kind": "t1",
"data": {
"author": "rocket_randall",
"body": "It's been a while since we had ours fixed, but we opted for a slow release pain killer that would keep them comfy for ~3 days after surgery while they healed. Of course our lunatics decided that once they were home and feeling no pain that it was time to lose their minds and nothing on earth could stop them running around and crashing into things. There was a bit of a scare after our little guy crashed into something and our daughter saw what looked like an eye injury, so off we went to the emergency vet just to be told that there was no injury, it was just the lubricant that was put on his eyes before surgery breaking up and he was fine. As a small consolation the vet said he was a huge endorphin hit for the staff at that time of night.",
"created_utc": 1786983276,
"score": 28,
"score_hidden": false,
"is_submitter": false,
"replies": {
"kind": "Listing",
"data": {
"children": [
{
"kind": "t1",
"data": {
"author": "teadazed",
"body": "Oh of course they must have eye drops during surgery, their eyes stay open don't they? It's the right way round to take them in for something that turns out to be nothing.\n\nIt's funny, I also have a collie who unlike the cats did not enjoy the aftermath of her neutering drugs At All, she was a haunted-looking cuddlebug until she could move properly again.",
"created_utc": 1786984556,
"score": 18,
"score_hidden": false,
"is_submitter": false,
"replies": {
"kind": "Listing",
"data": {
"children": [
{
"kind": "more",
"data": {
"count": 1,
"id": "p4dtjha"
}
}
]
}
}
}
}
]
}
}
}
},
{
"kind": "t1",
"data": {
"author": "Ashkir",
"body": "Some of the funniest things my dog has ever done was after they were waking up form anesthesia. She decided on a new place to potty that day too and to this day, years later, I cant train that spot out of her too 👀😭😂",
"created_utc": 1786975001,
"score": 112,
"score_hidden": false,
"is_submitter": false,
"replies": {
"kind": "Listing",
"data": {
"children": [
{
"kind": "t1",
"data": {
"author": "Winter_Fall_7066",
"body": "My dog was given a small sedative so the vet could witness a limp (he was excited and all over the place despite being in pain). Got home, ex asked “is the dog fucked up?” Dog immediately walks over to Christmas tree and pees on it. ",
"created_utc": 1786986392,
"score": 73,
"score_hidden": false,
"is_submitter": false,
"replies": {
"kind": "Listing",
"data": {
"children": [
{
"kind": "t1",
"data": {
"author": "Constant_Function238",
"body": "In his brain”thanks family for bringing my potty inside.”",
"created_utc": 1786990338,
"score": 28,
"score_hidden": false,
"is_submitter": false,
"replies": ""
}
}
]
}
}
}
},
{
"kind": "t1",
"data": {
"author": "Pr0digy_",
"body": "My friend years ago dropped off his mastiff to get neutered and I was the one to pick him up because my friend was working late, he was dopey and groggy but really happy to see me. I took him home and watched him until my friend came home as soon as my friend entered the house the dog growled (nothing crazy) and went and hid behind me it was hilarious. Everything was forgiven with some treats and pets. ",
"created_utc": 1787001797,
"score": 19,
"score_hidden": false,
"is_submitter": false,
"replies": {
"kind": "Listing",
"data": {
"children": [
{
"kind": "t1",
"data": {
"author": "werewooferer",
"body": "the dog knew who took him, and knew who saved him 🤣",
"created_utc": 1787167461,
"score": 3,
"score_hidden": false,
"is_submitter": false,
"replies": ""
}
}
]
}
}
}
}
]
}
}
}
},
{
"kind": "more",
"data": {
"count": 1,
"id": "p4c9qw2"
}
}
]
}
}
}
},
{
"kind": "t1",
"data": {
"author": "chickens_for_laughs",
"body": "I remember that vet telling me to not let her jump onto furniture. Like I could get her to stop. She healed fine and lived to 20!",
"created_utc": 1786979935,
"score": 22,
"score_hidden": false,
"is_submitter": false,
"replies": {
"kind": "Listing",
"data": {
"children": [
{
"kind": "t1",
"data": {
"author": "cAt_S0fa",
"body": "Lol yes the vet said the same thing to my mother when she got my childhood cat spayed. That night the cat was climbing the curtains...\n",
"created_utc": 1787000485,
"score": 3,
"score_hidden": false,
"is_submitter": false,
"replies": ""
}
}
]
}
}
}
},
{
"kind": "more",
"data": {
"count": 21,
"id": "p49jamx"
}
}
]
}
}
}
},
{
"kind": "more",
"data": {
"count": 24,
"id": "p4cqjj6"
}
}
]
}
}
}
},
{
"kind": "t1",
"data": {
"author": "Shailo",
"body": "What a precious baby 🥺 those eyes omg. Wishing her a speedy recovery and lots of cuddles and treats in the meantime 💖",
"created_utc": 1786960262,
"score": 667,
"score_hidden": false,
"is_submitter": false,
"replies": ""
}
},
{
"kind": "t1",
"data": {
"author": "Trenzane",
"body": "I misread it as Spa day and was like ❤️ \n\nThen my brain worked and went ☹️",
"created_utc": 1786962348,
"score": 875,
"score_hidden": false,
"is_submitter": false,
"replies": {
"kind": "Listing",
"data": {
"children": [
{
"kind": "t1",
"data": {
"author": "klgall1",
"body": "Glad I'm not the only one! I was reading the comments and saw the discussions about all the drugs and was very confused.\n\nI am not awake yet haha.",
"created_utc": 1786969784,
"score": 89,
"score_hidden": false,
"is_submitter": false,
"replies": ""
}
},
{
"kind": "t1",
"data": {
"author": "Cyber_Mermaid",
"body": "I still thought this said \"spa day\" until I read this comment lol",
"created_utc": 1786985184,
"score": 23,
"score_hidden": false,
"is_submitter": false,
"replies": ""
}
},
{
"kind": "more",
"data": {
"count": 8,
"id": "p48kfhn"
}
}
]
}
}
}
},
{
"kind": "more",
"data": {
"count": 195,
"id": "p4c6cph"
}
}
]
}
}
]