A shared Facebook link lands on a page that carries the whole post logged
out -- the caption, the author, the files, the dimensions -- in ScheduledServerJS
payloads that are plain JSON in ordinary script tags. What it does not carry
is only that post. A reel arrives with the next five reels of the feed
attached under viewer.lasso_blue_feed, a video with its related videos, and
every one of them has the same fields in the same shape as the real one.
Reading the first node with media on it gets a stranger's reel under someone
else's name, which is the same false attribution a lifted quote-post picture
used to be.
So nothing is read until the post has been picked out. partsOfPost matches the
id in the address against every node that names one; an address with no id in
it -- a pfbid permalink -- is matched on permalink_url instead. Only with
nothing to match on at all does it fall back to the route's query results,
which is still narrower than the whole payload: the page ships its entire
client configuration alongside the post, thousands of nodes carrying a name or
an id, and a plain search finds a video player setting long before it finds the
author.
An id that matches nothing is a failure rather than a best guess. Facebook
answers a link to something it no longer has by quietly serving something else
-- /watch/<id> for a video that is gone comes back as the Watch home page, feed
and all -- so the error card, which still carries the link and the copy button,
is the honest answer.
One post's pieces are spread across several payload blocks: a video post keeps
its files in one, its caption in another, its author's avatar in a third and
its timestamp in a fourth. Every claiming node is collected, not just the
first, and the author's gaps are filled only from nodes carrying the same id.
The author is whatever the payload calls the owner -- actors, owner,
video_owner, owner_as_page. `author` on a Facebook page means the author of a
comment, which sits right there in the same shape with a name and a picture of
its own.
Media is videoDeliveryLegacyFields.browser_native_hd_url with
preferred_thumbnail as its poster, photo_image or image for a picture, and
all_subattachments.nodes for a post of several -- which Facebook ships empty on
every single-picture post, so only a populated one is a carousel. The CDN is
signed and serves Range without asking for a referrer, but the assets are
proxied like everything else.
/share/{r,v,p,g} links are stubs, so the adapter follows one and hands back
where it landed: the share code says nothing about what it opens, and rdid,
share_url and fs are what the redirect leaves behind. m.facebook.com is a login
wall logged out, so the rewrite rule rebuilds on www.
Fixtures are real captures of a reel, a photo post and a video post, trimmed of
the DASH manifests and tracking blobs. The reel keeps its recommendations and
the video post keeps its comments, because those are the two things that have
to survive being read past.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# antisocial — working notes
|
||||
|
||||
A self-hosted page that shows a social post without the app. StopTheMadness rewrites
|
||||
links to X, Threads, Instagram, TikTok, Bluesky and Reddit into
|
||||
links to X, Threads, Instagram, Facebook, TikTok, Bluesky and Reddit into
|
||||
`/<prefix>/<original path>`; this resolves the post by driving a real headless Chromium
|
||||
and renders the media, the text, a platform badge and a copy-the-original button.
|
||||
|
||||
@@ -35,6 +35,7 @@ different fixes:
|
||||
| Right media, wrong count | The structured payload was missed and it fell through to the DOM, which only shows the first carousel item. |
|
||||
| Poster image where a video belongs | Same fall-through, plus the `<video>` had not hydrated when the DOM was read. |
|
||||
| Nothing at all | The platform refused this specific post logged out, or a key moved. |
|
||||
| Someone else's post entirely | Facebook. The linked post was not on the page, so the feed it ships alongside got read instead. Should be an error, not a post — check what `partsOfPost` matched. |
|
||||
| Reddit post fine, no comments | The `.json` was refused and it fell through to the page, where the comment tree loads late. |
|
||||
|
||||
**3. Look at what the page actually served.**
|
||||
@@ -89,6 +90,9 @@ Things worth knowing before editing:
|
||||
- **Extraction is layered**, most structured first: the platform's own API response
|
||||
caught in flight → an inline payload → the rendered DOM → OG tags. Keep that order
|
||||
when you touch an adapter; each layer is the fallback for the one above.
|
||||
- **`mediaFromOpenGraph` in `meta-media.ts`** is the floor shared by Instagram, Threads
|
||||
and Facebook. The three are one product underneath; the first two also share the media
|
||||
schema, Facebook has its own.
|
||||
- **`src/platforms/scan.ts`** pulls a balanced JSON object out of a page by key,
|
||||
including when it arrives escaped inside a JS string. Instagram needs this. If a
|
||||
payload looks present but parses to nothing, suspect an extra encoding level.
|
||||
@@ -172,6 +176,35 @@ Things worth knowing before editing:
|
||||
typed without a scheme counts as well, but only when it ends in an image extension —
|
||||
the rule wants a host, a path *and* that extension, because comments are full of
|
||||
dotted, slashed prose that must not turn into links.
|
||||
- **Facebook** — the post page logged out carries everything: the caption, the author,
|
||||
the files and the dimensions, in `ScheduledServerJS` payloads that are plain JSON in
|
||||
ordinary `<script>` tags. What it does *not* carry is only that post. A reel ships the
|
||||
next five reels of the feed under `viewer.lasso_blue_feed`, a video ships its related
|
||||
videos, and each one has the same fields in the same shape as the real one — so
|
||||
nothing may be read until the post has been picked out, which `partsOfPost` does by
|
||||
matching the id in the address against every node that names one. An address with no
|
||||
id in it (a `pfbid` permalink) is matched on `permalink_url`/`wwwURL` instead; only
|
||||
then, with nothing to match on at all, does it fall back to the route's query results,
|
||||
which is still narrower than the whole payload — the page ships its entire client
|
||||
configuration too, thousands of nodes carrying a `name` or an `id`, and a plain search
|
||||
finds a video player setting long before it finds the author. An id that matches
|
||||
nothing is a *failure*: Facebook answers a link to something it no longer has by
|
||||
quietly serving something else (`/watch/<id>` becomes the Watch home page, feed and
|
||||
all), and reading that would put a stranger's video under the link that was shared.
|
||||
One post's pieces are spread over several payload blocks — a video post keeps its
|
||||
files in one, its caption in another and its author's avatar in a third — so every
|
||||
claiming node is collected, not just the first. The author is whatever the payload
|
||||
calls the *owner* (`actors`, `owner`, `video_owner`, `owner_as_page`); `author` on a
|
||||
Facebook page means the author of a comment, which sits in the same shape with a name
|
||||
and a picture, and taking the first node with a name on it finds a commenter as
|
||||
readily as the poster. Media: `videoDeliveryLegacyFields.browser_native_hd_url` for a
|
||||
video with `preferred_thumbnail` as its poster, `photo_image`/`image` for a picture,
|
||||
and `all_subattachments.nodes` for a post of several — which is shipped empty on
|
||||
single-picture posts, so only a populated one is a carousel. The CDN is signed and
|
||||
serves `Range` without being asked for a referrer, but the assets are proxied like
|
||||
everything else. `/share/{r,v,p,g}/<code>` links are stubs; the adapter follows one
|
||||
and hands back where it landed, since the share code says nothing about what it opens.
|
||||
`m.facebook.com` is a login wall logged out — always rebuild on `www.`.
|
||||
- **Threads** — same media schema as Instagram (`src/platforms/meta-media.ts`). Its
|
||||
payloads are full of empty stub nodes, so the finder only accepts a node with actual
|
||||
candidates in it. The page ships the linked post, the author's follow-ups, other
|
||||
|
||||
Reference in New Issue
Block a user