Commit Graph
5 Commits
Author SHA1 Message Date
Kydoimosandanthropic/claude-opus-4-8 cc4f094fb8 Fall through to the post page when a reel embeds only its cover frame
CI / Typecheck, test, build (pull_request) Successful in 29s
The captioned embed stopped shipping a reel's video_url and never draws a
logged-out <video>, so both the payload and the DOM hand back the cover
frame alone. Because that still counts as media, the post-page fallback
never fired and a reel resolved to a still image.

Trigger the fallback when the URL is a reel (or the payload declared a
video) but no video was found, so it reaches the post page, whose payload
still carries the progressive file in video_versions. The embed's
caption, handle and avatar are carried forward, since the post-page
payload does not include the owner.

Co-authored-by: anthropic/claude-opus-4-8
2026-09-16 20:57:26 -03:00
Kydoimos 045a312f14 Point agent docs at AGENTS.md as the source of truth
CI / Typecheck, test, build (pull_request) Successful in 29s
2026-09-16 11:42:40 -03:00
KydoimosandClaude Opus 5 0a196454d1 Sign the automated version bump commit
Publish / Build and push (pull_request) Successful in 3s
Publish / Move the working version on (pull_request) Skipped
CI / Typecheck, test, build (pull_request) Successful in 15s
main requires signed commits, so the pull request the bump job opens after a
release cannot be merged while the commit in it is unsigned. The key the server
signs merge commits with is not reachable from a runner, so the job signs with a
dedicated release-bot SSH key that the instance trusts through
TRUSTED_SSH_KEYS — no bot account, because an account key is only consulted
after the web Verify flow and that flow has no API.

Inert until the key is trusted and RELEASE_BOT_SIGNING_KEY is set, and loudly so:
the step checks the secret and ssh-keygen before it starts, runs the key through
ssh-keygen -y so a truncated or re-wrapped one is caught as itself rather than as
"gpg failed to sign the data", and the commit is re-read for a gpgsig header
before it is pushed.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-12 11:58:47 -03:00
KydoimosandClaude Opus 5 ab23c2b9fe Add Facebook
CI / Typecheck, test, build (pull_request) Successful in 29s
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]>
2026-09-12 11:35:32 -03:00
KydoimosandClaude Opus 5 fa07b43c1c Share the link-preview fallback between the Meta adapters
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]>
2026-09-12 11:35:16 -03:00