Changeset e0a1ec1 in Klonkt


Ignore:
Timestamp:
06/30/2026 02:27:57 AM (2 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
d3b9f68
Parents:
084d1c0
Message:

feat(editor): per-post "share audio on the fediverse" toggle

Adds a per-post checkbox in the post editor that sets fedi_open on the post's hosted tracks
on save — so opening audio + federating it happens in one step, instead of toggling per track
in Beheer -> Audio and then re-saving the post. The underlying flag stays per track.

  • src/routes/posts.js — setAudioFediOpen()/postAudioFediOpen() helpers; create + save set fedi_open on the post's track/album/playlist tracks before federating; the edit form computes the checkbox's initial state
  • src/views/pages/post-edit.ejs — the fedi_open_audio checkbox next to NSFW/noindex
  • src/services/i18n.js — pedit.fedi_audio_label (nl/en/de)

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

Location:
src
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • src/routes/posts.js

    r084d1c0 re0a1ec1  
    169169
    170170// ==================== CREATE POST ====================
     171// ── Per-post audio federation ──────────────────────────────────────────────
     172// "Share audio on the fediverse" is a per-post choice in the editor, but the underlying
     173// flag is per track (audio_tracks.fedi_open — it gates the file + drives the AS2 Audio
     174// attachment). NB: the file gate is per file, so opening a track in one post makes its file
     175// fetchable for every post that reuses it.
     176function setAudioFediOpen(siteId, content, open) {
     177  const val = open ? 1 : 0;
     178  const c = content || '';
     179  try {
     180    for (const m of c.matchAll(/\[\[track:([A-Za-z0-9_-]+)\]\]/g)) db.prepare('UPDATE audio_tracks SET fedi_open = ? WHERE id = ? AND site_id = ?').run(val, m[1], siteId);
     181    for (const m of c.matchAll(/\[\[album:([^\]]+)\]\]/g)) db.prepare('UPDATE audio_tracks SET fedi_open = ? WHERE site_id = ? AND album = ?').run(val, siteId, m[1].trim());
     182    for (const m of c.matchAll(/\[\[playlist:([A-Za-z0-9_-]+)\]\]/g)) db.prepare('UPDATE audio_tracks SET fedi_open = ? WHERE id IN (SELECT track_id FROM playlist_tracks WHERE playlist_id = ?)').run(val, m[1]);
     183  } catch { /* non-fatal */ }
     184}
     185// True when the post references hosted audio AND all of it is currently fedi_open (drives the
     186// editor checkbox's initial state).
     187function postAudioFediOpen(siteId, content) {
     188  const c = content || '';
     189  if (!/\[\[(track|album|playlist):/i.test(c)) return false;
     190  let total = 0, open = 0;
     191  const tally = (r) => { if (r && r.media_id) { total++; if (r.fedi_open) open++; } };
     192  try {
     193    for (const m of c.matchAll(/\[\[track:([A-Za-z0-9_-]+)\]\]/g)) tally(db.prepare('SELECT fedi_open, media_id FROM audio_tracks WHERE id = ? AND site_id = ?').get(m[1], siteId));
     194    for (const m of c.matchAll(/\[\[album:([^\]]+)\]\]/g)) for (const r of db.prepare('SELECT fedi_open, media_id FROM audio_tracks WHERE site_id = ? AND album = ? AND media_id IS NOT NULL').all(siteId, m[1].trim())) tally(r);
     195    for (const m of c.matchAll(/\[\[playlist:([A-Za-z0-9_-]+)\]\]/g)) for (const r of db.prepare('SELECT t.fedi_open, t.media_id FROM playlist_tracks pt JOIN audio_tracks t ON t.id = pt.track_id WHERE pt.playlist_id = ? AND t.media_id IS NOT NULL').all(m[1])) tally(r);
     196  } catch { /* non-fatal */ }
     197  return total > 0 && open === total;
     198}
     199
    171200router.post('/posts/create', requireAuth, (req, res) => {
    172201  const site = res.locals.site;
     
    228257  );
    229258
     259  // Per-post "share audio on the fediverse" → set fedi_open on this post's hosted tracks
     260  // BEFORE federating, so the Create note carries the right Audio attachments.
     261  setAudioFediOpen(site.id, cleanContent, req.body.fedi_open_audio);
     262
    230263  if (finalStatus === 'published') {
    231264    try {
     
    278311    post,
    279312    isNew: false,
     313    fediOpenAudio: postAudioFediOpen(site.id, post.content),
    280314    pageTitle: 'Edit: ' + (post.title || 'Untitled'),
    281315    bodyClass: 'on-special',
     
    349383    finalSlug, publishedAt, now, post.id
    350384  );
     385
     386  // Per-post "share audio on the fediverse" → set fedi_open on this post's hosted tracks
     387  // BEFORE federating, so the Update/Create note carries the right Audio attachments.
     388  setAudioFediOpen(site.id, cleanContent, req.body.fedi_open_audio);
    351389
    352390  // Update FTS
  • src/services/i18n.js

    r084d1c0 re0a1ec1  
    694694    'pedit.pin_top': 'bovenaan',
    695695    'pedit.pin_nth_suffix': 'e van boven',
    696     'pedit.noindex_label': 'noindex (verberg voor zoekmachines)', 'pedit.nsfw_label': 'NSFW / gevoelige inhoud', 'pedit.nsfw_cw_ph': 'Waarschuwingstekst (optioneel, standaard: Gevoelige inhoud)', 'post.nsfw_warning': 'Gevoelige inhoud', 'post.nsfw_show': 'Tonen',
     696    'pedit.noindex_label': 'noindex (verberg voor zoekmachines)', 'pedit.nsfw_label': 'NSFW / gevoelige inhoud', 'pedit.fedi_audio_label': 'Audio openbaar delen op de fediverse (speelt inline in apps; bestand downloadbaar)', 'pedit.nsfw_cw_ph': 'Waarschuwingstekst (optioneel, standaard: Gevoelige inhoud)', 'post.nsfw_warning': 'Gevoelige inhoud', 'post.nsfw_show': 'Tonen',
    697697    'pedit.fan_only_label': 'Alleen voor ingelogde fans',
    698698    'pedit.schedule_label': 'Publicatie inplannen',
     
    16091609    'pedit.pin_top': 'at the top',
    16101610    'pedit.pin_nth_suffix': 'th from top',
    1611     'pedit.noindex_label': 'noindex (hide from search engines)', 'pedit.nsfw_label': 'NSFW / sensitive content', 'pedit.nsfw_cw_ph': 'Warning text (optional, default: Sensitive content)', 'post.nsfw_warning': 'Sensitive content', 'post.nsfw_show': 'Show',
     1611    'pedit.noindex_label': 'noindex (hide from search engines)', 'pedit.nsfw_label': 'NSFW / sensitive content', 'pedit.fedi_audio_label': 'Share audio openly on the fediverse (plays inline in apps; file downloadable)', 'pedit.nsfw_cw_ph': 'Warning text (optional, default: Sensitive content)', 'post.nsfw_warning': 'Sensitive content', 'post.nsfw_show': 'Show',
    16121612    'pedit.fan_only_label': 'Logged-in fans only',
    16131613    'pedit.schedule_label': 'Schedule publication',
     
    25242524    'pedit.pin_top': 'ganz oben',
    25252525    'pedit.pin_nth_suffix': '. von oben',
    2526     'pedit.noindex_label': 'noindex (vor Suchmaschinen verbergen)', 'pedit.nsfw_label': 'NSFW / sensibler Inhalt', 'pedit.nsfw_cw_ph': 'Warntext (optional, Standard: Sensibler Inhalt)', 'post.nsfw_warning': 'Sensibler Inhalt', 'post.nsfw_show': 'Anzeigen',
     2526    'pedit.noindex_label': 'noindex (vor Suchmaschinen verbergen)', 'pedit.nsfw_label': 'NSFW / sensibler Inhalt', 'pedit.fedi_audio_label': 'Audio offen im Fediverse teilen (spielt inline in Apps; Datei herunterladbar)', 'pedit.nsfw_cw_ph': 'Warntext (optional, Standard: Sensibler Inhalt)', 'post.nsfw_warning': 'Sensibler Inhalt', 'post.nsfw_show': 'Anzeigen',
    25272527    'pedit.fan_only_label': 'Nur für angemeldete Fans',
    25282528    'pedit.schedule_label': 'Veröffentlichung planen',
  • src/views/pages/post-edit.ejs

    r084d1c0 re0a1ec1  
    245245               placeholder="<%= t('pedit.nsfw_cw_ph') %>"
    246246               style="width:100%;box-sizing:border-box;margin:6px 0 2px;font-size:13px;padding:7px 9px">
     247        <label class="pe-checkbox">
     248          <input type="checkbox" name="fedi_open_audio" value="1" <%= (typeof fediOpenAudio !== 'undefined' && fediOpenAudio) ? 'checked' : '' %>>
     249          <span>🌐 <%= t('pedit.fedi_audio_label') %></span>
     250        </label>
    247251        <script>
    248252        (function () {
Note: See TracChangeset for help on using the changeset viewer.