Changeset 9026de5 in Klonkt


Ignore:
Timestamp:
08/16/2026 02:25:04 PM (3 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
749605a
Parents:
7090fd2
Message:

Album: published en released lenen de datum van de uitgavepost

Robins voorstel, en mijn eerste antwoord was te voorzichtig. Ik wilde
released leeg laten omdat een plaat uit 2018 die je vandaag post dan
zou beweren vandaag uit te komen. Dat gebeurt ook -- maar bij de meeste
Klonkt-sites IS de post het uitbrengen, en GEEN datum is slechter dan
een datum die op het gewone geval klopt. Het handmatige veld is precies
het gereedschap voor de uitzondering, en dat wint altijd.

year vult nog steeds niets aan, en dat is geen inconsequentie: een
jaartal is geen dag, terwijl de postdatum een gebeurtenis is die
werkelijk heeft plaatsgevonden. Verzinnen versus afleiden.

GEPLANDE POSTS, Robins tweede vraag. Het pad klopt op alle drie de
schakels: zolang de post 'scheduled' is vindt uitgavePost hem niet (die
filtert op status='published') en heeft de plaat dus nog geen
uitgavedatum -- hij is immers nog niet uit. De Scheduler zet daarna
published_at op COALESCE(published_at, publish_at, CURRENT_TIMESTAMP),
oftewel op de GEPLANDE tijd, en wij lezen COALESCE(published_at,
created_at). Een server die een uur plat lag levert dus geen uur te late
uitgavedatum op, en created_at wint nooit bij een gepubliceerde post.
Er staat nu een test die precies dat afloopt, met een worker die expres
te laat draait.

uitgavePost wordt in buildPlaylistCollection nog EEN keer aangeroepen en
twee keer gebruikt: het Album leent er zijn datums en titel van,
leenVanPost zijn tekst en tags. Twee losse aanroepen zouden uiteen kunnen
lopen, en dan staat er weer iets anders op het ingesloten object dan op
zijn eigen URI -- de fout van een uur geleden.

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

Files:
2 edited

Legend:

Unmodified
Added
Removed
  • src/services/music/index.js

    r7090fd2 r9026de5  
    485485  if (!pl) return null;
    486486  const abs = (u) => !u ? null : (/^https?:/i.test(u) ? u : `${base}${u.startsWith('/') ? '' : '/'}${u}`);
    487   // GEEN epoch als terugval. `published` is bij hen verplicht, maar 1970 is een
    488   // ANTWOORD en geen ontbrekend veld -- en dat is erger: een lezer kan een gat
    489   // opmerken, een leugen niet. playlists.created_at heeft een default, dus als
    490   // hij hier ontbreekt is er een leespad dat de kolom laat vallen. Dat willen we
    491   // zien, niet maskeren. (Zo kwam op 16-8 de route boven water die id, title,
    492   // artist, year, cover_url en kind selecteerde en de rest niet.)
    493   const wanneer = pl.created_at ? new Date(pl.created_at).toISOString() : null;
     487  // WANNEER IS DEZE PLAAT GEPUBLICEERD. De post die hem uitbrengt gaat voor, en
     488  // niet als noodgreep maar omdat hij het beter weet: playlists.created_at is
     489  // het moment waarop de RIJ is aangemaakt, en dat kan weken eerder zijn terwijl
     490  // je nog aan het samenstellen was. AS2 `published` vraagt wanneer het object
     491  // openbaar werd, en dat is de post.
     492  //
     493  // GEEN epoch als laatste terugval. `published` is bij hen verplicht, maar 1970
     494  // is een ANTWOORD en geen ontbrekend veld -- en dat is erger: een lezer kan een
     495  // gat opmerken, een leugen niet. Zo kwam op 16-8 de route boven water die id,
     496  // title, artist, year, cover_url en kind selecteerde en de rest niet.
     497  //
     498  // OOK VOOR `released`, en daar had ik het eerst mis (Robin, 16-8). Mijn
     499  // bezwaar was: post je vandaag een plaat uit 2018, dan beweert dit dat hij
     500  // vandaag uitkwam. Dat gebeurt ook -- maar bij de meeste Klonkt-sites IS de
     501  // post het uitbrengen, en GEEN datum is slechter dan een datum die op het
     502  // gewone geval klopt. Het handmatige veld is precies het gereedschap voor de
     503  // uitzondering: bij een heruitgave vul je hem in en die wint.
     504  const postDatum = (pl._post && pl._post.uit_wanneer) ? new Date(pl._post.uit_wanneer) : null;
     505  const wanneer = postDatum ? postDatum.toISOString()
     506    : (pl.created_at ? new Date(pl.created_at).toISOString() : null);
    494507  // Dezelfde lening als in buildPlaylistCollection: de post die de plaat
    495508  // uitbrengt geeft zijn titel, en de eigen titel blijft als alsoKnownAs staan.
     
    504517  };
    505518  if (titel !== pl.title) album.alsoKnownAs = pl.title;
    506   // `released` alleen als er een ECHTE datum is. `year` vult hem niet aan: een
    507   // jaartal is geen dag, en dat is de reden dat release_date bestaat.
     519  // Het ingevulde veld wint altijd; anders de DAG waarop de post verscheen.
     520  // `year` vult hem nog steeds niet aan, en dat is geen inconsequentie: een
     521  // jaartal is geen dag, terwijl de postdatum een gebeurtenis is die werkelijk
     522  // heeft plaatsgevonden. Het verschil is verzinnen versus afleiden.
    508523  if (pl.release_date) album.released = pl.release_date;
     524  else if (postDatum) album.released = postDatum.toISOString().slice(0, 10);
    509525  if (pl.mb_release_id) album.musicbrainzId = pl.mb_release_id;
    510526  const hoes = abs(pl.cover_url || null);
     
    522538  const hostPosts = site.id ? trackHostPosts(site.id) : null;
    523539  const albums = site.id ? trackAlbums(site.id) : null;
     540  // EEN keer opzoeken en twee keer gebruiken: het Album leent er zijn datums en
     541  // titel van, leenVanPost onderaan zijn tekst en tags. Twee losse aanroepen
     542  // zouden niet alleen dubbel werk zijn maar ook uiteen kunnen lopen -- en dan
     543  // staat er weer iets anders op het ingesloten object dan op zijn eigen URI.
     544  const post = site.id ? uitgavePost(site.id, playlist.id) : null;
    524545  const items = (rows || []).map((r) => buildTrackAudio(base, site, r, { coverFallback: playlist.cover_url || null, hostPosts, albums }));
    525546  const out = pagedCollection(`${actorId(base, site.slug)}/playlists/${playlist.id}`, items, {
     
    547568  // staat toch al ingesloten op de track.
    548569  if ((playlist.kind || 'album') === 'album') {
    549     const album = buildAlbumObject(base, site, playlist);
     570    const album = buildAlbumObject(base, site, { ...playlist, _post: post });
    550571    for (const veld of ['published', 'released', 'musicbrainzId', 'artist_credit', 'image']) {
    551572      if (album[veld] !== undefined) out[veld] = album[veld];
    552573    }
    553574  }
    554   return leenVanPost(base, site, out, uitgavePost(site.id, playlist.id));
     575  return leenVanPost(base, site, out, post);
    555576}
    556577
     
    577598  try {
    578599    const rijen = db.prepare(`
    579       SELECT id, slug, title, excerpt, content, cover_image_url, tags
     600      SELECT id, slug, title, excerpt, content, cover_image_url, tags,
     601             COALESCE(published_at, created_at) AS uit_wanneer
    580602      FROM posts
    581603      WHERE site_id = ? AND status = 'published'
  • test/ap-fw-track.test.js

    r7090fd2 r9026de5  
    204204  assert.equal(col.artist_credit, undefined);
    205205});
     206
     207test('published EN released lenen de datum van de uitgavepost', () => {
     208  // De post is het moment van uitbrengen. Geen datum is slechter dan een datum
     209  // die op het gewone geval klopt; voor een heruitgave vul je het veld in.
     210  db.prepare(`INSERT INTO posts (id, site_id, author_id, slug, title, content, status, published_at)
     211              VALUES ('p-uit','s1','u1','de-plaat-uit','De Plaat Is Er','<p>[[playlist:de-plaat]]</p>','published','2026-05-04T12:00:00Z')`).run();
     212  // De fixture zette hierboven al een release_date; die wint terecht, dus voor
     213  // DEZE test moet hij weg. (Dat de eerste versie hier omviel is het bewijs dat
     214  // de voorrang werkt.)
     215  db.prepare("UPDATE playlists SET release_date = NULL WHERE id = 'de-plaat'").run();
     216  const al = audio('t1').track.album;
     217  assert.equal(al.published, '2026-05-04T12:00:00.000Z');
     218  assert.equal(al.released, '2026-05-04', 'released is een DAG, geen tijdstip');
     219});
     220
     221test('een ingevulde uitgavedatum wint van de postdatum', () => {
     222  // Dit is het geval waarvoor het veld bestaat: oud werk dat je vandaag post.
     223  db.prepare("UPDATE playlists SET release_date = '2018-09-01' WHERE id = 'de-plaat'").run();
     224  const al = audio('t1').track.album;
     225  assert.equal(al.released, '2018-09-01', 'de postdatum overschreef het ingevulde veld');
     226  db.prepare("UPDATE playlists SET release_date = NULL WHERE id = 'de-plaat'").run();
     227});
     228
     229test('geen post en geen veld: dan geen released', () => {
     230  // Afwezig is afwezig -- we leiden af waar er iets af te leiden valt, en
     231  // verzinnen niets waar dat niet zo is.
     232  db.prepare("INSERT INTO playlists (id, site_id, title, kind) VALUES ('kaal','s1','Kale Plaat','album')").run();
     233  db.prepare("INSERT INTO playlist_tracks (playlist_id, track_id, position) VALUES ('kaal','t9',1)").run();
     234  const al = audio('t9').track.album;
     235  assert.equal(al.name, 'Kale Plaat');
     236  assert.equal(al.released, undefined);
     237});
     238
     239test('een GEPLANDE post: de geplande dag telt, niet wanneer de worker draaide', () => {
     240  // Robins vraag (16-8). Drie schakels moeten kloppen:
     241  //
     242  //   1. zolang de post 'scheduled' is vindt uitgavePost hem NIET (die filtert
     243  //      op status='published'), dus de plaat heeft nog geen uitgavedatum --
     244  //      hij is immers nog niet uit;
     245  //   2. de Scheduler zet published_at op COALESCE(published_at, publish_at,
     246  //      CURRENT_TIMESTAMP), dus op de GEPLANDE tijd;
     247  //   3. wij lezen COALESCE(published_at, created_at) en komen daar dus op uit.
     248  //
     249  // Dat derde punt is waarom created_at hier nooit wint: bij een gepubliceerde
     250  // post staat published_at altijd gevuld. En punt 2 is waarom een server die
     251  // een uur plat lag geen uur te late uitgavedatum oplevert.
     252  db.prepare("INSERT INTO playlists (id, site_id, title, kind) VALUES ('gepland','s1','Geplande Plaat','album')").run();
     253  db.prepare("INSERT INTO playlist_tracks (playlist_id, track_id, position) VALUES ('gepland','t2',1)").run();
     254  db.prepare(`INSERT INTO posts (id, site_id, author_id, slug, title, content, status, publish_at, created_at, published_at)
     255              VALUES ('p-gepland','s1','u1','komt-nog','Komt Nog','<p>[[playlist:gepland]]</p>','scheduled',
     256                      '2026-12-24T09:00:00Z','2026-06-01T08:00:00Z',NULL)`).run();
     257
     258  // Nog gepland: geen datum, want de plaat is nog niet uit.
     259  assert.equal(audio('t2').track.album.released, undefined, 'een geplande post bracht al iets uit');
     260
     261  // De Scheduler doet zijn werk -- en draait expres LATER dan gepland.
     262  db.prepare(`UPDATE posts SET status = 'published',
     263              published_at = COALESCE(published_at, publish_at, CURRENT_TIMESTAMP) WHERE id = 'p-gepland'`).run();
     264
     265  const al = audio('t2').track.album;
     266  assert.equal(al.released, '2026-12-24', 'niet de geplande dag');
     267  assert.equal(al.published, '2026-12-24T09:00:00.000Z');
     268  assert.notEqual(al.released, '2026-06-01', 'de creatiedatum lekte door');
     269});
Note: See TracChangeset for help on using the changeset viewer.