Changeset d18c60e in Klonkt for src/services/ActivityPubService.js
- Timestamp:
- 07/01/2026 06:55:46 PM (2 months ago)
- Branches:
- main
- Children:
- 0688b5f
- Parents:
- 43273c9
- File:
-
- 1 edited
-
src/services/ActivityPubService.js (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/ActivityPubService.js
r43273c9 rd18c60e 262 262 // An animated cover federates as the muted loop MP4 (→ a Video attachment): animated WebP is 263 263 // unreliable on Mastodon and its iOS apps; the MP4 plays everywhere. Else the still cover image. 264 if (post.cover_video_url && !noImages) urls.push(abs(post.cover_video_url)); 265 else if (post.cover_image_url && !noImages) urls.push(abs(post.cover_image_url)); 264 // Each entry carries the media URL + its alt text (federated as the AS2 attachment `name`, for a11y). 265 if (post.cover_video_url && !noImages) urls.push({ url: abs(post.cover_video_url), name: post.cover_alt || '' }); 266 else if (post.cover_image_url && !noImages) urls.push({ url: abs(post.cover_image_url), name: post.cover_alt || '' }); 266 267 let body = post.content || ''; 267 268 // Only federate inline images we can actually serve: absolute http(s) URLs, or our own 268 269 // /media/ uploads. A relative path we don't host (e.g. a stale /images/... ref) would 404 269 // and show up as a black tile in Mastodon's attachment grid. 270 if (!noImages) for (const m of body.matchAll(/<img\b[^>]*\bsrc="([^"]+)"[^>]*>/gi)) { 271 const src = m[1]; 272 if (/^https?:\/\//i.test(src) || src.startsWith('/media/')) urls.push(abs(src)); 270 // and show up as a black tile in Mastodon's attachment grid. Carry the <img alt="…"> through 271 // as the attachment description. 272 if (!noImages) for (const m of body.matchAll(/<img\b[^>]*>/gi)) { 273 const tag = m[0]; 274 const src = (tag.match(/\bsrc="([^"]+)"/i) || [])[1]; 275 if (!src || !(/^https?:\/\//i.test(src) || src.startsWith('/media/'))) continue; 276 const alt = (tag.match(/\balt="([^"]*)"/i) || [])[1] || ''; 277 urls.push({ url: abs(src), name: alt }); 273 278 } 274 279 body = body.replace(/<img\b[^>]*>/gi, ''); … … 345 350 } 346 351 const seen = new Set(); 347 const attachment = urls.filter( Boolean)348 .filter(( u) => { if (seen.has(u)) return false; seen.add(u); return true; })349 .map(( u) => { const mt = mediaType(u); // specific AS2 subtype (Image/Audio/Video) over generic Document352 const attachment = urls.filter((x) => x && x.url) 353 .filter((x) => { if (seen.has(x.url)) return false; seen.add(x.url); return true; }) 354 .map((x) => { const mt = mediaType(x.url); // specific AS2 subtype (Image/Audio/Video) over generic Document 350 355 const ty = /^image\//i.test(mt) ? 'Image' : /^video\//i.test(mt) ? 'Video' : /^audio\//i.test(mt) ? 'Audio' : 'Document'; 351 return { type: ty, mediaType: mt, url: u }; }); 356 const a = { type: ty, mediaType: mt, url: x.url }; 357 if (x.name) a.name = String(x.name).slice(0, 1500); // alt text / description (AS2 `name`) 358 return a; }); 352 359 for (const a of openAudio) attachment.push(a); // fedi_open tracks → native Audio players 353 360 … … 385 392 if (post.cover_image_url && noImages) { 386 393 const cov = abs(post.cover_image_url); 387 if (cov) note.image = { type: 'Image', mediaType: mediaType(cov), url: cov };394 if (cov) { note.image = { type: 'Image', mediaType: mediaType(cov), url: cov }; if (post.cover_alt) note.image.name = String(post.cover_alt).slice(0, 1500); } 388 395 } 389 396 // Experiment (mirrors PeerTube / schema.org `embedUrl`): point at the GATED player page
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)