Changeset 0677bac in Klonkt


Ignore:
Timestamp:
08/09/2026 08:24:16 AM (4 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
d60c498
Parents:
a5d14c7
Message:

De tags kwamen er als onzin uit, en de tekst helemaal niet (shaer-38y)

Live nagekeken op dev, en twee dingen klopten niet.

DE TAGS. posts.tags staat als JSON-ARRAY in de database, niet als kommalijst.
Mijn eigen split leverde #["Doen we Niet" en #"music" op -- geen fout die
omvalt, een die onzin uitlevert. Nu met normalizeTags en tagParts, dezelfde
parser als de rest, dus ook dezelfde CamelCase voor tags van meerdere woorden.

Die twee stonden in ActivityPubService en music/ mag daar niet uit importeren,
dus ze zijn naar ap-core verhuisd -- de plek voor precies dit soort gedeelde
grondstof. Een kopie maken zou hetzelfde uit elkaar lopen opleveren als bij de
afleidingsregel.

DE TEKST. De excerpt bleek in de praktijk vaak leeg, en dan kwam er geen
content mee terwijl de post wel tekst heeft. Nu: excerpt eerst -- die is
geschreven om samen te vatten -- en anders het lijf zelf, zonder shortcodes
(die zijn de muziek, niet het verhaal erover) en zonder opmaak. Blijft er niets
over, dan geen veld: dat is beter dan een leeg veld.

De test gebruikte de kommavorm en testte dus de terugval in plaats van de echte
weg. Nu JSON, zoals het er staat.

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

Files:
4 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    ra5d14c7 r0677bac  
    2929import Blocklist from './BlocklistService.js';
    3030import * as Guardianship from './guardianship/index.js';
    31 import { PUBLIC, AP_CONTEXT, safeUrl, actorId, noteId, guessMediaType } from './ap-core.js';
     31import { PUBLIC, AP_CONTEXT, safeUrl, actorId, noteId, guessMediaType, normalizeTags, tagParts } from './ap-core.js';
    3232// Doorgeven wat hier altijd vandaan kwam, zodat elke bestaande aanroep blijft werken.
    3333export { AP_CONTEXT, actorId, noteId, guessMediaType };
     
    29732973
    29742974// Normalise a post's tags field (array, JSON-string, or comma-string) to an array.
    2975 function normalizeTags(t) {
    2976   if (Array.isArray(t)) return t;
    2977   if (typeof t === 'string') {
    2978     const s = t.trim(); if (!s) return [];
    2979     if (s[0] === '[') { try { const a = JSON.parse(s); return Array.isArray(a) ? a : []; } catch { /* fall through */ } }
    2980     return s.split(',').map((x) => x.trim()).filter(Boolean);
    2981   }
    2982   return [];
    2983 }
     2975// normalizeTags en tagParts staan sinds shaer-38y in ap-core: music/ heeft ze
     2976// ook nodig en mag hier niet uit importeren.
    29842977// A tag → { label, slug }. Multi-word tags become CamelCase (#LiveMusic) for the display
    29852978// name (Mastodon hashtags can't contain spaces; CamelCase is the accessibility norm); the
    29862979// slug/href stays lowercase ("livemusic").
    2987 function tagParts(raw) {
    2988   const words = String(raw || '').trim().split(/[\s_]+/).map((w) => w.replace(/[^\p{L}\p{M}\p{N}]/gu, '')).filter(Boolean);
    2989   if (!words.length) return null;
    2990   const slug = words.join('').toLowerCase();
    2991   if (!slug) return null;
    2992   const label = words.length > 1 ? words.map((w) => w[0].toUpperCase() + w.slice(1)).join('') : words[0];
    2993   return { label, slug };
    2994 }
    29952980// Merge a post's tags field + the #hashtags linked inline in its body into one deduped
    29962981// Hashtag tag list (with hrefs to our /tag page).
  • src/services/ap-core.js

    ra5d14c7 r0677bac  
    9393  })[(e || '').toLowerCase()] || 'image/jpeg';
    9494}
     95
     96/**
     97 * Het tagveld van een post als lijst. Het staat in de database als JSON-ARRAY
     98 * en niet als kommalijst -- op komma's splitsen levert `#["Doen we Niet"` op,
     99 * en dat faalt niet, het liegt. Vandaar een echte parser, met de kommavorm als
     100 * terugval voor wat er handmatig is ingevuld.
     101 */
     102export function normalizeTags(t) {
     103  if (Array.isArray(t)) return t;
     104  if (typeof t === 'string') {
     105    const s = t.trim(); if (!s) return [];
     106    if (s[0] === '[') { try { const a = JSON.parse(s); return Array.isArray(a) ? a : []; } catch { /* dan toch als kommalijst */ } }
     107    return s.split(',').map((x) => x.trim()).filter(Boolean);
     108  }
     109  return [];
     110}
     111
     112/**
     113 * Een tag -> { label, slug }. Tags van meerdere woorden worden CamelCase
     114 * (#LiveMusic) voor de weergavenaam -- een Mastodon-hashtag mag geen spaties
     115 * bevatten en CamelCase is daar de toegankelijkheidsnorm; de slug en de href
     116 * blijven kleingeschreven ("livemusic").
     117 */
     118export function tagParts(raw) {
     119  const words = String(raw || '').trim().split(/[\s_]+/).map((w) => w.replace(/[^\p{L}\p{M}\p{N}]/gu, '')).filter(Boolean);
     120  if (!words.length) return null;
     121  const slug = words.join('').toLowerCase();
     122  if (!slug) return null;
     123  const label = words.length > 1 ? words.map((w) => w[0].toUpperCase() + w.slice(1)).join('') : words[0];
     124  return { label, slug };
     125}
  • src/services/music/index.js

    ra5d14c7 r0677bac  
    1414
    1515import db from '../../config/database.js';
    16 import { AP_CONTEXT, PUBLIC, actorId, noteId, safeUrl, guessMediaType } from '../ap-core.js';
     16import { AP_CONTEXT, PUBLIC, actorId, noteId, safeUrl, guessMediaType, normalizeTags, tagParts } from '../ap-core.js';
    1717import { afleidenUitInsluitingen, ingeslotenPlaylists } from '../../assets/js/shared/post-music-type.js';
    1818
     
    368368  // samenvatting en content het lijf. Artiest en jaar blijven dus in summary
    369369  // staan -- dat is een samenvatting, en de posttekst is dat niet.
    370   const tekst = (post.excerpt || '').trim();
     370  const tekst = tekstVanPost(post);
    371371  if (tekst) obj.content = tekst;
    372372
     
    388388}
    389389
    390 /** De tags van een post als AS2 Hashtags. Zelfde vorm als buildHashtagList. */
     390/**
     391 * De tags van een post als AS2 Hashtags -- zelfde vorm als buildHashtagList.
     392 *
     393 * Met normalizeTags en niet met een eigen split: het veld staat als JSON-array
     394 * in de database, en op komma's splitsen leverde live `#["Doen we Niet"` op.
     395 * Dat is het soort fout dat niet omvalt maar onzin uitlevert.
     396 */
    391397function hashtagsVanPost(base, tagsField) {
    392   const ruw = Array.isArray(tagsField)
    393     ? tagsField
    394     : String(tagsField || '').split(',');
    395398  const uit = [], gezien = new Set();
    396   for (const t of ruw) {
    397     const label = String(t || '').trim().replace(/^#/, '');
    398     if (!label) continue;
    399     const slug = label.toLowerCase().replace(/\s+/g, '-');
    400     if (gezien.has(slug)) continue;
    401     gezien.add(slug);
    402     uit.push({ type: 'Hashtag', href: `${base}/tag/${encodeURIComponent(slug)}`, name: '#' + label });
     399  for (const t of normalizeTags(tagsField)) {
     400    const p = tagParts(t);
     401    if (!p || gezien.has(p.slug)) continue;
     402    gezien.add(p.slug);
     403    uit.push({ type: 'Hashtag', href: `${base}/tag/${encodeURIComponent(p.slug)}`, name: '#' + p.label });
    403404  }
    404405  return uit;
     406}
     407
     408/**
     409 * De tekst van een post, als er een is. De excerpt heeft voorrang -- die is
     410 * geschreven om samen te vatten. Staat die leeg, dan het lijf zelf: zonder
     411 * shortcodes (die zijn de muziek, niet het verhaal erover) en zonder opmaak.
     412 * Levert null als er niets overblijft, want een leeg veld is slechter dan geen.
     413 */
     414function tekstVanPost(post) {
     415  const excerpt = String(post.excerpt || '').trim();
     416  if (excerpt) return excerpt;
     417  const kaal = String(post.content || '')
     418    .replace(/\[\[[a-z]+:[^\]]*\]\]/gi, ' ')
     419    .replace(/<[^>]+>/g, ' ')
     420    .replace(/&nbsp;/gi, ' ')
     421    .replace(/&[a-z#0-9]+;/gi, ' ')
     422    .replace(/\s+/g, ' ')
     423    .trim();
     424  return kaal || null;
    405425}
    406426
  • test/music-uitgave.test.js

    ra5d14c7 r0677bac  
    5151  excerpt: 'Opgenomen op zolder, in een week.',
    5252  cover: '/media/hoes.jpg',
    53   tags: 'lofi, zolder',
     53  tags: '["lofi","zolder tapes"]',
    5454});
    5555
     
    7474  assert.equal(col.image.type, 'Image');
    7575  assert.equal(col.image.url, `${BASE}/media/hoes.jpg`);
    76   assert.deepEqual(col.tag.map((t) => t.name), ['#lofi', '#zolder']);
     76  // Het tagveld is JSON in de database, en een tag van twee woorden wordt
     77  // CamelCase -- zelfde regel als elders, want het is nu dezelfde parser.
     78  assert.deepEqual(col.tag.map((t) => t.name), ['#lofi', '#ZolderTapes']);
     79  assert.equal(col.tag[1].href, `${BASE}/tag/zoldertapes`);
    7780  assert.equal(col.url, `${BASE}/drie-nieuwe`);
    7881  assert.equal(col.context, `${BASE}/ap/notes/p-los`);
     
    153156  assert.equal(M.uitgavePost(SITE, 'tweede'), null);
    154157});
     158
     159test('zonder excerpt leent hij de tekst uit het lijf van de post', () => {
     160  // Live bleek de excerpt vaak leeg; dan is `content` weglaten slechter dan de
     161  // tekst zelf pakken. Shortcodes gaan eruit -- die zijn de muziek, niet het
     162  // verhaal erover.
     163  const p = maakPost({
     164    id: 'p-tekst', slug: 'zonder-excerpt', titel: 'Zonder excerpt',
     165    content: '<p>Drie schetsen van vorige week.</p>[[track:t-een]]<div>&nbsp;</div>',
     166  });
     167  const col = M.buildPostTrackCollection(BASE, site, p);
     168  assert.equal(col.content, 'Drie schetsen van vorige week.');
     169});
     170
     171test('en een post die alleen uit shortcodes bestaat leent geen lege tekst', () => {
     172  const p = maakPost({
     173    id: 'p-kaal', slug: 'alleen-muziek', titel: 'Alleen muziek',
     174    content: '[[track:t-twee]]<div> </div>',
     175  });
     176  const col = M.buildPostTrackCollection(BASE, site, p);
     177  assert.equal(col.content, undefined, 'een leeg veld is slechter dan geen veld');
     178});
Note: See TracChangeset for help on using the changeset viewer.