Changeset 1a0b007 in Klonkt


Ignore:
Timestamp:
06/30/2026 02:50:15 PM (2 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
d4d8f9d
Parents:
443f982
Message:

feat(federation): link-only tracks federate their external links (Mastodon cards the player)

A track that's link-only (external Spotify/YouTube/SoundCloud, no hosted file) used to
federate as a "listen on <site>" link + the cover. Now it emits the track's external link(s) in
the note and drops the cover, so Mastodon cards the first (Spotify -> its player) and shows the
rest as clickable links — the fediverse-native version of the on-site track card. Hosted/playable
tracks (own player card) and posts without an external track are unchanged; Klonkt's on-site card
is unchanged.

  • src/services/ActivityPubService.js — buildNote collects link-only track external links, suppresses cover, emits links

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    r443f982 r1a0b007  
    236236    return false;
    237237  })();
    238   const noImages = playable || hasEmbed; // suppress image attachments → let the player/embed card show
     238  // Link-only tracks (external Spotify/YouTube/SoundCloud, no hosted file): collect their links
     239  // so we federate them — Mastodon cards the first (its player), the rest show as clickable links
     240  // — instead of a bare "listen on site" link, and we suppress the cover so the card can show.
     241  const trackEmbedLinks = (() => {
     242    if (playable) return [];
     243    const out = [];
     244    try {
     245      for (const m of (post.content || '').matchAll(/\[\[track:([A-Za-z0-9_-]+)\]\]/g)) {
     246        const r = db.prepare('SELECT media_id, link_spotify, link_youtube, link_soundcloud FROM audio_tracks WHERE id = ?').get(m[1]);
     247        if (r && !r.media_id) for (const u of [r.link_spotify, r.link_youtube, r.link_soundcloud]) if (u && /^https?:\/\//i.test(u)) out.push(u);
     248      }
     249    } catch { /* non-fatal */ }
     250    return [...new Set(out)].slice(0, 6);
     251  })();
     252  const noImages = playable || hasEmbed || trackEmbedLinks.length > 0; // suppress images → let the player/embed card show
    239253  const urls = [];
    240254  // Posts with PLAYABLE hosted audio suppress image attachments so Mastodon renders
     
    292306  if (hadAudio) {
    293307    const lbl = audioLabels.length ? esc(audioLabels.slice(0, 4).join(', ')) : '';
    294     // For playable posts, append a version param to the listen-link so Mastodon
    295     // sees a NEW card URL and re-crawls it (fresh SQUARE player card) instead of
    296     // reusing the cached landscape one. Invisible: the link TEXT stays clean, the
    297     // page ignores the param. Bump FEDI_CARD_VER when the card dimensions change.
    298     const listenHref = playable ? `${human}?fc=${FEDI_CARD_VER}` : human;
    299     body += `<p>🎵 ${lbl ? `<strong>${lbl}</strong> — ` : ''}<a href="${listenHref}">listen on ${esc(site.title || 'the site')}</a></p>`;
     308    if (trackEmbedLinks.length) {
     309      // Link-only track(s): emit the external link(s). Mastodon cards the first (Spotify → its
     310      // player), the rest render as clickable links — the fediverse-native "embed + links".
     311      body += `<p>🎵 ${lbl ? `<strong>${lbl}</strong>` : ''}</p>`;
     312      for (const u of trackEmbedLinks) { const eu = esc(u); body += `<p><a href="${eu}">${eu}</a></p>`; }
     313    } else {
     314      // For playable posts, append a version param to the listen-link so Mastodon
     315      // sees a NEW card URL and re-crawls it (fresh SQUARE player card) instead of
     316      // reusing the cached landscape one. Invisible: the link TEXT stays clean, the
     317      // page ignores the param. Bump FEDI_CARD_VER when the card dimensions change.
     318      const listenHref = playable ? `${human}?fc=${FEDI_CARD_VER}` : human;
     319      body += `<p>🎵 ${lbl ? `<strong>${lbl}</strong> — ` : ''}<a href="${listenHref}">listen on ${esc(site.title || 'the site')}</a></p>`;
     320    }
    300321  }
    301322  // Klonkt renders post content with white-space:pre-wrap, so raw newlines ARE line
Note: See TracChangeset for help on using the changeset viewer.