Changeset 9a585bf in Klonkt


Ignore:
Timestamp:
08/16/2026 01:29:11 PM (3 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
21257e4
Parents:
38d5e40
Message:

Track: published en artist_credit erbij

Twee van de vijf gaten naar Funkwhale's bibliotheek-ingest.

published was een omissie in de kleine Emissary-vorm, niet een keuze:
MusicEntitySerializer (regel 1278) eist het en we hebben created_at.

artist_credit leek de onmogelijke. Hun keten is Track -> ArtistCredit ->
Artist, en elke schakel wil id, name en published -- terwijl een artiest
bij ons een tekstkolom is. De MusicBrainz-koppeling van vorige week liet
zien dat dat niet klopt: de site-ACTOR is de artiest. Een echt,
opvraagbaar adres, met de sitetitel als naam en een musicbrainzId zodra
hij gekoppeld is. Er valt niets te verzinnen, en het is niet nieuw --
open.audio leidde op 13-8 al zelf een artist_credit af uit onze
attributedTo. We maken expliciet wat daar toch al gebeurde.

De artiestnaam van de track gaat naar credit en niet naar de entiteit.
Dat is waar hun model de credittekst verwacht, en er een id per
artiestnaam van maken zou identiteit uit een string zijn -- dezelfde fout
die we bij het album vermijden (shaer-756s).

@container: @list op artist_credit is geen opsmuk: ze lezen het veld met
first_attr(FW.artist_credit, "@list"), en zonder die declaratie
expandeert onze array er niet naar. Dan staat er iets dat er goed uitziet
en dat hun lezer niet vindt -- precies het soort stil gat waar deze week
al twee keer een dag in ging zitten.

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

Files:
3 edited

Legend:

Unmodified
Added
Removed
  • src/services/ap-core.js

    r38d5e40 r9a585bf  
    8888    Track: 'fw:Track',
    8989    track: { '@id': 'fw:track', '@type': '@id' },
     90    // ARTIEST-CREDIT. Hun TrackSerializer eist minstens een artist_credit, en
     91    // dat leek lang onmogelijk: het vraagt een Artist met een eigen id, en bij
     92    // ons was een artiest tekst. Sinds de MusicBrainz-koppeling (shaer-mbz) is
     93    // dat niet meer waar -- de site-ACTOR is de artiest. Een echte, opvraagbare
     94    // URI, met de sitetitel als naam en een musicbrainzId als hij gekoppeld is.
     95    // Er valt hier niets te verzinnen; open.audio leidde dit zelfs al zelf af
     96    // uit onze attributedTo (gemeten 13-8).
     97    //
     98    // `@container: @list` is GEEN opsmuk. Ze lezen dit veld met
     99    // first_attr(FW.artist_credit, "@list"), en zonder die declaratie expandeert
     100    // onze array niet naar een @list -- dan staat er iets dat er goed uitziet en
     101    // door hun lezer niet gevonden wordt. Letterlijk hun contexts.py regel 311.
     102    Artist: 'fw:Artist',
     103    ArtistCredit: 'fw:ArtistCredit',
     104    artist: { '@id': 'fw:artist', '@type': '@id' },
     105    artist_credit: { '@id': 'fw:artist_credit', '@type': '@id', '@container': '@list' },
     106    credit: 'fw:credit',
     107    musicbrainzId: 'fw:musicbrainzId',
    90108    position: 'schema:position',
    91109    bitrate: 'schema:bitrate',
  • src/services/music/index.js

    r38d5e40 r9a585bf  
    1414
    1515import db from '../../config/database.js';
    16 import { AP_CONTEXT, PUBLIC, actorId, noteId, safeUrl, guessMediaType, buildHashtagList, pagedCollection } from '../ap-core.js';
     16import { AP_CONTEXT, PUBLIC, actorId, noteId, safeUrl, guessMediaType, buildHashtagList, pagedCollection, isMbid } from '../ap-core.js';
    1717import { afleidenUitInsluitingen, ingeslotenPlaylists } from '../../assets/js/shared/post-music-type.js';
    1818// De luisteraars horen bij de muziekkant; hier doorgegeven zodat
     
    195195  // een tekstkolom; er hier een adres van maken zou een ding beloven dat niet
    196196  // bestaat. Zie shaer-k37k -- dat is de keuze die daarvoor eerst moet vallen.
     197  //
     198  // WIE IS DE ARTIEST. Hun Artist is een ENTITEIT met een id, en bij ons is een
     199  // artiest een tekstkolom op de track. Die twee verzoenen we zo: de entiteit
     200  // is de site-ACTOR -- een echt, opvraagbaar adres, het account dat dit
     201  // uitbrengt -- en de tekst uit de kolom gaat naar `credit`, want dat is
     202  // precies waar hun model de credittekst verwacht.
     203  //
     204  // Dat is eerlijk en het is niet nieuw: open.audio leidde op 13-8 al zelf een
     205  // artist_credit af uit onze attributedTo. We maken alleen expliciet wat daar
     206  // toch al gebeurde.
     207  //
     208  // DE GRENS ERVAN: brengt een site werk van iemand anders uit, dan zegt dit
     209  // dat de site de artiest is. Dat stond al in attributedTo, dus we maken het
     210  // niet erger -- maar het is wel de reden dat we hier geen id per artiestnaam
     211  // verzinnen. Identiteit uit een string is dezelfde fout als bij het album
     212  // (shaer-756s).
     213  const wanneer = r.created_at ? new Date(r.created_at).toISOString()
     214    : (site.created_at ? new Date(site.created_at).toISOString() : new Date(0).toISOString());
     215  const artiest = {
     216    type: 'Artist',
     217    id: actorId(base, site.slug),
     218    name: site.title || site.slug,
     219    published: site.created_at ? new Date(site.created_at).toISOString() : wanneer,
     220  };
     221  if (isMbid(site.mb_artist_id)) artiest.musicbrainzId = String(site.mb_artist_id).trim().toLowerCase();
     222
    197223  a.track = {
    198224    type: 'Track',
    199225    id: `${a.id}#track`,
    200226    name: a.name,
     227    published: wanneer,
    201228    ...(Number(r.position) ? { position: Number(r.position) } : {}),
     229    artist_credit: [{
     230      type: 'ArtistCredit',
     231      id: `${a.id}#artist-credit`,
     232      published: wanneer,
     233      artist: artiest,
     234      ...(r.artist ? { credit: r.artist } : {}),
     235    }],
    202236  };
    203237  if (r.duration) a.duration = `PT${Math.round(r.duration)}S`;
  • test/ap-fw-track.test.js

    r38d5e40 r9a585bf  
    8484  for (const a of col.orderedItems) assert.equal(a.track.type, 'Track', `${a.name} zonder track`);
    8585});
     86
     87// ── artist_credit (shaer-3f8a, het gat naar hun ingest) ──────────────
     88//
     89// TrackSerializer (regel 1569) eist `artist_credit` met min_length=1, en elke
     90// ArtistCredit eist een Artist die zelf id, name en published nodig heeft
     91// (MusicEntitySerializer, regel 1278). Dat leek onmogelijk zolang een artiest
     92// bij ons tekst was -- tot de MusicBrainz-koppeling liet zien dat de ACTOR de
     93// artiest is.
     94
     95test('de track draagt een artist_credit met een echte Artist', () => {
     96  const a = audio('t1');
     97  assert.ok(Array.isArray(a.track.artist_credit), 'geen lijst');
     98  assert.equal(a.track.artist_credit.length, 1);
     99  const ac = a.track.artist_credit[0];
     100  assert.equal(ac.type, 'ArtistCredit');
     101  assert.ok(ac.id && ac.published, 'ArtistCredit mist id of published');
     102  assert.equal(ac.artist.type, 'Artist');
     103  assert.equal(ac.artist.id, `${BASE}/ap/users/band`, 'de actor IS de artiest');
     104  assert.equal(ac.artist.name, 'De Band');
     105  assert.ok(ac.artist.published, 'Artist mist published');
     106});
     107
     108test('de artiestnaam van de track gaat naar credit, niet naar de entiteit', () => {
     109  // De kolom is een credittekst, geen identiteit. Er een id van maken zou
     110  // dezelfde fout zijn als bij het album (shaer-756s).
     111  assert.equal(audio('t1').track.artist_credit[0].credit, 'De Band');
     112  assert.equal(audio('t2').track.artist_credit[0].credit, undefined, 'geen artiest, geen credit');
     113});
     114
     115test('de track heeft published -- MusicEntitySerializer eist het', () => {
     116  assert.ok(audio('t1').track.published);
     117});
     118
     119test('artist_credit is een @list in de context, anders vindt hun lezer niets', async () => {
     120  // Ze lezen dit met first_attr(FW.artist_credit, "@list"). Zonder de
     121  // container-declaratie expandeert onze array niet naar een @list en staat er
     122  // iets dat er goed uitziet en niet gevonden wordt.
     123  const core = await import('../src/services/ap-core.js');
     124  const term = core.AP_CONTEXT.find((x) => x && typeof x === 'object' && x.artist_credit);
     125  assert.deepEqual(term.artist_credit, { '@id': 'fw:artist_credit', '@type': '@id', '@container': '@list' });
     126  assert.equal(term.ArtistCredit, 'fw:ArtistCredit');
     127  assert.equal(term.Artist, 'fw:Artist');
     128});
     129
     130test('musicbrainzId komt mee zodra de site gekoppeld is', () => {
     131  assert.equal(audio('t1').track.artist_credit[0].artist.musicbrainzId, undefined);
     132  db.prepare("UPDATE sites SET mb_artist_id = '5441c29d-3602-4898-b1a1-b77fa23b8e50' WHERE id = 's1'").run();
     133  const s2 = db.prepare("SELECT * FROM sites WHERE id = 's1'").get();
     134  const a = AP.buildTrackAudio(BASE, s2, AP.openTrack('s1', 't1'), { hostPosts: null });
     135  assert.equal(a.track.artist_credit[0].artist.musicbrainzId, '5441c29d-3602-4898-b1a1-b77fa23b8e50');
     136  db.prepare("UPDATE sites SET mb_artist_id = NULL WHERE id = 's1'").run();
     137});
Note: See TracChangeset for help on using the changeset viewer.