Changeset 7d19465 in Klonkt


Ignore:
Timestamp:
07/17/2026 02:27:52 AM (8 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
f1a23b8
Parents:
92a2c46
git-author:
Robin <roboburr@…> (07/17/2026 02:27:34 AM)
git-committer:
Robin <roboburr@…> (07/17/2026 02:27:52 AM)
Message:

Fix: boosted video-only posts keep their media in the Circle (Loops)

Boosting a video-only post lost its media twice over: resolveRemoteNote only
surfaced image/* attachments (images list), so upsertBoostedNote stored
media_json=[] for a Loops video; and its refresh-UPDATE clobbered an existing
good media_json (cached via following) with that empty set. resolveRemoteNote
now also returns full typed media via mediaFromNote (images stays image-only
for the interact preview); upsertBoostedNote prefers the typed media and its
refresh keeps cached media when the resolve yielded none. Verified live: the
reported Loops file is 2.9MB with moov in reach, and a seeded boosted video row
now renders a grid tile with a real generated poster (200 image/webp, 13KB).
Covered by test/boost-media.test.js (63 tests green). Reported by Robin.

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

Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • CHANGELOG.de.md

    r92a2c46 r7d19465  
    2121
    2222### Behoben
     23- **Ein geboosteter Videobeitrag behält sein Video im Zirkel.** Das Boosten
     24  eines Nur-Video-Beitrags (Loops.video) speicherte ihn ohne Medien, sodass der
     25  Zirkel eine nackte Textkachel statt eines Video-Thumbnails zeigte; erneutes
     26  Boosten konnte das Video sogar aus einer bereits gecachten Kopie löschen.
     27  Boosts tragen jetzt die vollen typisierten Medien, und ein Refresh löscht
     28  gecachte Medien nicht mehr.
    2329- **Der Titel der Interaktionsseite folgt deiner Sprache.** "Interacteer via de
    2430  fediverse" stand fest auf Niederländisch im Browser-Tab, auch auf einer
  • CHANGELOG.md

    r92a2c46 r7d19465  
    2020
    2121### Fixed
     22- **A boosted video post keeps its video in the Circle.** Boosting a video-only
     23  post (Loops.video) stored it without its media, so the Circle showed a bare
     24  text tile instead of a video thumbnail; re-boosting could even wipe the video
     25  from an already-cached copy. Boosts now carry the full typed media, and a
     26  refresh never erases cached media.
    2227- **The interact page title follows your language.** "Interacteer via de
    2328  fediverse" was hardcoded Dutch in the browser tab, even on an English site.
  • CHANGELOG.nl.md

    r92a2c46 r7d19465  
    2020
    2121### Opgelost
     22- **Een geboostte videopost houdt zijn video in de Cirkel.** Een video-only post
     23  (Loops.video) boosten sloeg hem op zonder media, waardoor de Cirkel een kale
     24  teksttegel toonde in plaats van een videothumbnail; opnieuw boosten kon de
     25  video zelfs wissen uit een al-gecachte kopie. Boosts dragen nu de volledige
     26  getypeerde media mee, en een refresh wist gecachte media nooit meer.
    2227- **De titel van de interactiepagina volgt je taal.** "Interacteer via de
    2328  fediverse" stond hardcoded in het Nederlands in het browsertabblad, ook op een
  • src/services/ActivityPubService.js

    r92a2c46 r7d19465  
    18281828    cw: note.summary || '',
    18291829    images,
     1830    // Full typed media (incl. video/mp4) for the timeline cache. `images` above is
     1831    // image-only for the interact page preview; a boosted video-only post (Loops)
     1832    // lost its media entirely because upsertBoostedNote only saw `images`.
     1833    media: mediaFromNote(note),
    18301834    threadInboxes,                                          // every ancestor author's inbox
    18311835    localPostId: localTgt ? localTgt.post_id : '',          // our post this belongs to (if any)
     
    20142018  if (!slug || !note || !note.object_uri) return;
    20152019  const id = note.object_uri;
    2016   const media = JSON.stringify((note.images || []).map((u) => ({ url: u, type: 'image/jpeg' })));
     2020  // Prefer the full typed media (incl. video/mp4 — a Loops boost is video-only and
     2021  // rendered a bare text tile); fall back to the image-only list for older callers.
     2022  const media = (note.media && note.media !== '[]')
     2023    ? note.media
     2024    : JSON.stringify((note.images || []).map((u) => ({ url: u, type: 'image/jpeg' })));
    20172025  try {
    20182026    const r = tlStmts().ins.run(id, slug, note.actor_uri || '', note.actor_name || '', note.actor_handle || '',
     
    20232031      // resolved note. Without this a row cached without its cover (or with
    20242032      // stale content) stayed stale forever — even boosting again didn't heal it.
    2025       db.prepare('UPDATE ap_timeline SET content = ?, media_json = ?, nsfw = ?, cw = ?, url = COALESCE(?, url) WHERE slug = ? AND id = ?')
    2026         .run(note.content || '', media, note.sensitive ? 1 : 0, note.cw || null, note.url || null, slug, id);
     2033      // Keep the CACHED media when the resolve yielded none: an empty re-resolve
     2034      // used to clobber a good media_json (the followed copy had the video, the
     2035      // boost wiped it to []).
     2036      db.prepare(`UPDATE ap_timeline SET content = ?, media_json = CASE WHEN ? = '[]' THEN media_json ELSE ? END,
     2037                  nsfw = ?, cw = ?, url = COALESCE(?, url) WHERE slug = ? AND id = ?`)
     2038        .run(note.content || '', media, media, note.sensitive ? 1 : 0, note.cw || null, note.url || null, slug, id);
    20272039    }
    20282040  } catch { /* ignore */ }
Note: See TracChangeset for help on using the changeset viewer.