Fall through to the post page when a reel embeds only its cover frame
CI / Typecheck, test, build (pull_request) Successful in 48s

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
This commit is contained in:
2026-09-16 20:54:39 -03:00
co-authored by anthropic/claude-opus-4-8
parent 2782ba41b3
commit f968ac749c
3 changed files with 82 additions and 3 deletions
+19 -3
View File
@@ -254,15 +254,31 @@ async function resolve(ctx: ResolveContext): Promise<Post> {
if (scraped && !scraped.text && dom.text) scraped.text = dom.text;
if (scraped && !scraped.handle && dom.handle) scraped.handle = dom.handle;
if (!scraped?.media.length) {
// The embed no longer ships a reel's `video_url`, and its player never
// draws a `<video>` logged out — so both payload and DOM hand back only
// the cover frame. An image where a video belongs is a miss, not a result:
// fall through to the post page, whose payload still carries
// `video_versions`. Keep the embed's caption/handle/avatar either way.
const hasVideo = scraped?.media.some((item) => item.kind === 'video') === true;
if (!scraped?.media.length || (expectsVideo && !hasVideo)) {
// The embed refuses some posts outright ("the link may be broken").
// Try the post itself: its payload first, then its link preview.
const carry = scraped;
await page.goto(originalUrl, { waitUntil: 'domcontentloaded' }).catch(() => undefined);
scraped = fromPayloads(await scriptTexts(ctx)) ?? scraped;
const fromPost = fromPayloads(await scriptTexts(ctx));
if (fromPost?.media.length) {
scraped = {
...fromPost,
...(fromPost.text ? {} : carry?.text ? { text: carry.text } : {}),
...(fromPost.handle ? {} : carry?.handle ? { handle: carry.handle } : {}),
...(fromPost.avatar ? {} : carry?.avatar ? { avatar: carry.avatar } : {}),
...(fromPost.displayName ? {} : carry?.displayName ? { displayName: carry.displayName } : {}),
};
}
if (!scraped?.media.length) {
const og = await fromOpenGraph(ctx);
if (og.media.length) scraped = { ...og, ...(scraped?.handle ? { handle: scraped.handle } : {}) };
if (og.media.length) scraped = { ...og, ...(carry?.handle ? { handle: carry.handle } : {}) };
}
}