Add Facebook #17

Merged
thatguygriff merged 2 commits from facebook-posts into main 2026-09-12 14:43:21 +00:00
Member

Adds Facebook as a platform, under /fb. One file in src/platforms/, one row
in the table, and the rewrite rule in the README.

$ npm run resolve -- 'https://www.facebook.com/share/r/1FF2LPQhgf/?mibextid=wwXIfr'
originalUrl  https://www.facebook.com/reel/962551420197596/
author       @thespeechprof · SpeechProf · avatar
text         "I need answers."
media        video 720x1280, 15s, with poster
postedAt     2026-08-30T15:38:45.000Z

Verified end to end through the route as well: badge, avatar, caption, and a
<video> whose /m/ proxy answers 206 Partial Content, so the scrubber
seeks. A reel, a pfbid photo post, a /photo/?fbid= permalink and a
/<page>/videos/<slug>/<id>/ post all resolve.

Picking the right post

The interesting part. A Facebook reel page ships the next five reels of the
feed in exactly the same shape as the linked one, and a video post scatters its
own pieces across four separate payload blocks — media in one, caption in
another, avatar in a third, timestamp in a fourth. Reading the first node that
carries media puts a stranger's video under someone else's name, which is the
same false attribution the lifted quote-post picture used to be.

So nothing is read until the post has been identified, and an id that matches
nothing is a failure rather than a best guess: /watch/<id> for a video
Facebook no longer has comes back as the Watch home page, feed and all. Before
that guard it cheerfully returned a stranger's reel; now it renders the error
card, which still carries the link and the copy button.

The author is whatever the payload calls the owner. author on a Facebook
page means the author of a comment, sitting right there in the same shape with
a name and a picture of its own — the video-post.json fixture keeps its
comments so that trap stays tested.

Three things worth a reviewer's attention

  1. Multi-photo posts are the one path not confirmed against a live link.
    all_subattachments.nodes is handled and unit-tested both ways — the empty
    stub Facebook ships on every post versus a real carousel — but five public
    pages searched had no album post in their logged-out feed. If one comes back
    as a single picture, that is where to look.
  2. Instagram and Threads are touched. Both carried a byte-identical Open
    Graph fallback; a third copy for Facebook is where it stops being a
    coincidence, so it moved into meta-media.ts with tests of its own. Split
    into its own commit.
  3. fb.watch short links get no rewrite rule. The host is in matchesHost
    so the CLI takes one, but a bare path segment on facebook.com is a profile
    name, so it cannot be rebuilt from a rewritten path the way TikTok's vm.
    codes are. Facebook issues /share/ links now. Easy to add later if they
    still turn up.

npm test — 158 pass, 0 fail. npm run typecheck clean.

🤖 Generated with Claude Code

Adds Facebook as a platform, under `/fb`. One file in `src/platforms/`, one row in the table, and the rewrite rule in the README. ``` $ npm run resolve -- 'https://www.facebook.com/share/r/1FF2LPQhgf/?mibextid=wwXIfr' originalUrl https://www.facebook.com/reel/962551420197596/ author @thespeechprof · SpeechProf · avatar text "I need answers." media video 720x1280, 15s, with poster postedAt 2026-08-30T15:38:45.000Z ``` Verified end to end through the route as well: badge, avatar, caption, and a `<video>` whose `/m/` proxy answers `206 Partial Content`, so the scrubber seeks. A reel, a `pfbid` photo post, a `/photo/?fbid=` permalink and a `/<page>/videos/<slug>/<id>/` post all resolve. ## Picking the right post The interesting part. A Facebook reel page ships the next five reels of the feed in exactly the same shape as the linked one, and a video post scatters its own pieces across four separate payload blocks — media in one, caption in another, avatar in a third, timestamp in a fourth. Reading the first node that carries media puts a stranger's video under someone else's name, which is the same false attribution the lifted quote-post picture used to be. So nothing is read until the post has been identified, and an id that matches nothing is a failure rather than a best guess: `/watch/<id>` for a video Facebook no longer has comes back as the Watch home page, feed and all. Before that guard it cheerfully returned a stranger's reel; now it renders the error card, which still carries the link and the copy button. The author is whatever the payload calls the *owner*. `author` on a Facebook page means the author of a comment, sitting right there in the same shape with a name and a picture of its own — the `video-post.json` fixture keeps its comments so that trap stays tested. ## Three things worth a reviewer's attention 1. **Multi-photo posts are the one path not confirmed against a live link.** `all_subattachments.nodes` is handled and unit-tested both ways — the empty stub Facebook ships on *every* post versus a real carousel — but five public pages searched had no album post in their logged-out feed. If one comes back as a single picture, that is where to look. 2. **Instagram and Threads are touched.** Both carried a byte-identical Open Graph fallback; a third copy for Facebook is where it stops being a coincidence, so it moved into `meta-media.ts` with tests of its own. Split into its own commit. 3. **`fb.watch` short links get no rewrite rule.** The host is in `matchesHost` so the CLI takes one, but a bare path segment on `facebook.com` is a profile name, so it cannot be rebuilt from a rewritten path the way TikTok's `vm.` codes are. Facebook issues `/share/` links now. Easy to add later if they still turn up. `npm test` — 158 pass, 0 fail. `npm run typecheck` clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Kydoimos added 2 commits 2026-09-12 14:35:56 +00:00
Instagram and Threads both ended with the same twelve lines: read the Open
Graph tags, make og:video a video with og:image as its poster, otherwise make
og:image an image, and attach the referrer headers the CDN wants. Facebook
needs the same floor for the same reason, and three copies of a thing is where
it stops being a coincidence.

meta-media.ts already exists because these platforms are one product
underneath. The media schema it normalizes is Instagram's and Threads';
Facebook's is its own, but the headers and the link preview are shared with
both, so that is where this goes.

Co-Authored-By: Claude Opus 5 <[email protected]>
Add Facebook
CI / Typecheck, test, build (pull_request) Successful in 29s
ab23c2b9fe
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]>
thatguygriff merged commit 4f60367e2f into main 2026-09-12 14:43:21 +00:00
thatguygriff deleted branch facebook-posts 2026-09-12 14:43:21 +00:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Unsupervised/antisocial#17