Ignore:
Timestamp:
07/28/2026 08:22:37 AM (6 weeks ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
c52dc82
Parents:
b258a79
Message:

Embeds toonden niets: OpenGraph erbij en een backfill

Twee oorzaken, allebei mijn eigen gat.

OPENGRAPH ONTBRAK. Ik had oEmbed gebouwd als de generieke weg, maar het grootste
deel van het web levert dat helemaal niet. Link-previews met een thumbnail
draaien in de praktijk op OpenGraph, en dat is ook wat Mastodon leest. Gecheckt
op een URL uit onze eigen feed: og:image en og:title wel, oEmbed niet. Zonder die
stap loste vrijwel elke link op naar kind=link en werd er dus niets opgeslagen,
en bleef de kaart leeg. OpenGraph zit nu in dezelfde keten, na oEmbed en voor de
kale link, en gebruikt dezelfde pagina-fetch: het kost geen extra request. De
fetch stuurt nu ook een User-Agent mee, want een deel van het web geeft z-n
og-tags alleen aan iets dat op een browser lijkt.

GEEN BACKFILL. embed_json werd alleen gevuld bij nieuwe binnenkomende posts, dus
alle 359 bestaande rijen bleven leeg: precies wat je ziet als je de app opent.
Self-heal v16 haalt het alsnog op, alleen voor rijen zonder quote en zonder
embed, dus een pagina-fetch per kandidaat en nooit opnieuw.

Changed files:
src/services/EmbedResolver.js

  • findOpenGraph (og:image/title/site_name, met twitter:-fallback)
  • resolveEmbed: OpenGraph na oEmbed, een pagina-fetch voor beide
  • liveIO: User-Agent op de pagina-fetch

src/services/ActivityPubService.js

  • SELFHEAL_VERSION 15 -> 16: embed_json backfillen

test/embed-resolver.test.js

  • 4 tests: og lezen + entities, twitter-fallback en non-http image geweigerd, oEmbed wint nog steeds van OpenGraph, en een og-only pagina levert een kaart

remarks: 213 tests groen. End-to-end gedraaid op een echte feed-URL: titel,
provider en thumbnail komen eruit.

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    rb258a79 r0101d0a  
    28492849// during a flux window, e.g. a fleet-wide update), and drops notes that are gone
    28502850// (404/410). Bump SELFHEAL_VERSION only on a release that warrants a re-sync.
    2851 const SELFHEAL_VERSION = 15; // v15: re-capture emoji_json/link_json for boosts that arrived before the Announce-path tag fix
     2851const SELFHEAL_VERSION = 16; // v16: resolve external link previews (embed_json) for posts that predate the embed pipeline
    28522852async function fetchNoteAP(url) {
    28532853  try {
     
    31593159    if (cur >= SELFHEAL_VERSION) return; // already healed for this version — skip on normal boots
    31603160    let rows = [];
    3161     try { rows = db.prepare('SELECT id, slug, content, media_json, nsfw, cw, url, emoji_json, link_json, quote_json, author_uri, author_name, author_emoji_json, reblog_name, reblog_handle, reblog_emoji_json FROM ap_timeline ORDER BY rowid DESC LIMIT 200').all(); } catch { /* no table */ }
     3161    try { rows = db.prepare('SELECT id, slug, content, media_json, nsfw, cw, url, emoji_json, link_json, quote_json, author_uri, author_name, author_emoji_json, reblog_name, reblog_handle, reblog_emoji_json, embed_json FROM ap_timeline ORDER BY rowid DESC LIMIT 200').all(); } catch { /* no table */ }
    31623162    let healed = 0, failed = 0;
    31633163    for (const r of rows) {
     
    31853185          const ai = actorInfo(await fetchActor(r.author_uri), r.author_uri);
    31863186          if (ai.emojis) { try { db.prepare('UPDATE ap_timeline SET author_emoji_json = ? WHERE id = ?').run(JSON.stringify(ai.emojis), r.id); } catch { /* ignore */ } }
     3187        }
     3188        // v16: link previews. A post from before the embed pipeline has no
     3189        // card at all, which is why nothing showed. Only for rows that have no
     3190        // quote (a quote already IS the card) and no embed yet, so this costs
     3191        // one page fetch per candidate and never repeats.
     3192        if (!r.quote_json && !r.embed_json) {
     3193          const ej = await resolveExternalEmbed(html || r.content).catch(() => null);
     3194          if (ej) { try { db.prepare('UPDATE ap_timeline SET embed_json = ? WHERE id = ?').run(ej, r.id); } catch { /* ignore */ } }
    31873195        }
    31883196        // v14: same for the booster's display name ("X boosted"). The row stores
Note: See TracChangeset for help on using the changeset viewer.