source: Klonkt/src/services/MusicBrainzService.js@ f59a46c

main
Last change on this file since f59a46c was f59a46c, checked in by Robin <roboburr@…>, 4 weeks ago

Stap 3: de koppeling gaat de draad op als schema:sameAs (shaer-mbz)

Alleen als de artiest ZELF gekozen heeft. Staat er niets, dan staat er niets --
een lege of geraden verwijzing is erger dan geen.

WAAROM sameAs EN NIET alsoKnownAs, en dat is geen smaakkwestie. alsoKnownAs is
in AS2 gereserveerd voor vroegere IDENTITEITEN van dezelfde actor, en FEP-7628
leunt erop bij een verhuizing: het oude adres controleert of het nieuwe hem daar
noemt voordat het volgers meegeeft. Een MBID daartussen zetten zou een
verhuizing kunnen laten mislukken. schema:sameAs zegt precies het goede: dit
ding is ook bekend onder die URI.

Geen nieuwe namespace: schema stond al in onze context, naast license, position
en bitrate. Dit is er een termregel bij.

EEN KRINGLOOP VERMEDEN. MusicBrainzService leent safeFetch uit
ActivityPubService, dus die er andersom uit laten importeren zou een cirkel
maken. isMbid en artiestUrl zijn pure vormcontroles zonder afhankelijkheden en
wonen nu in ap-core -- dezelfde plek en dezelfde reden als de tagparsers van
gisteren. MusicBrainzService exporteert ze her, zodat een aanroeper er niet over
hoeft na te denken.

DE ACTOR IS DE LAATSTE DEUR. Het scherm zeeft al op een echte MBID, maar
artiestUrl doet het nog een keer: wat hier langskomt gaat naar iedereen, en een
half adres is erger dan geen. Een test zet met opzet rommel in de kolom en eist
dat er niets naar buiten gaat.

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

  • Property mode set to 100644
File size: 4.4 KB
RevLine 
[a117862]1/**
2 * Een artiest zoekt zichzelf op in MusicBrainz (shaer-mbz).
3 *
4 * WAAROM DIT GEEN DIALECT IS. Funkwhale's Track/Artist/ArtistCredit zijn hun
5 * eigen vocabulaire -- hun docs noemen ze letterlijk "Custom Funkwhale object"
6 * -- en wij kunnen ze niet eerlijk vullen: artiest en album zijn bij ons
7 * tekstkolommen, geen entiteiten. Een MBID is iets anders: geen vocabulaire
8 * maar een REGISTER. Ernaar verwijzen is als een ISBN noemen. Je neemt niemands
9 * model over en je wijst naar iets dat al bestaat.
10 *
11 * WAT HIER NIET GEBEURT: schrijven. Via hun API zijn alleen tags, ratings,
12 * ISRC's en barcodes in te dienen -- artiesten, releases en recordings niet,
13 * dat gaat via hun website. Wij lezen dus alleen, en dat is meteen de
14 * geruststelling: we kunnen hun register niet vervuilen.
15 *
16 * TWEE HARDE REGELS VAN HUN KANT, allebei hieronder ingebakken omdat ze bij
17 * overtreding tot blokkade leiden en niet tot een foutmelding:
18 * - hoogstens EEN verzoek per seconde, per applicatie (niet per bezoeker)
19 * - een echte User-Agent, met contactgegevens
20 */
21import { safeFetch } from './ActivityPubService.js';
[f59a46c]22// De twee pure vormcontroles wonen in ap-core: ActivityPubService heeft ze ook
23// nodig voor de actor, en zonder die verhuizing zou dat een KRINGLOOP zijn --
24// deze module leent immers safeFetch dáár.
25import { isMbid, artiestUrl } from './ap-core.js';
[a117862]26
27const BASIS = 'https://musicbrainz.org/ws/2';
28
29/**
30 * De User-Agent die MusicBrainz eist. Hun regel: naam, versie en een manier om
31 * contact op te nemen. Een lege of generieke string is precies waarop ze
32 * blokkeren, dus als er geen contact is ingesteld zeggen we dat met zoveel
33 * woorden in plaats van iets aardigs te verzinnen.
34 */
35function userAgent() {
36 const contact = (process.env.MUSICBRAINZ_CONTACT || '').trim()
37 || (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, '')
38 || 'geen-contact-ingesteld';
39 return `Klonkt/1.0 ( ${contact} )`;
40}
41
42/**
43 * Hun tempo aanhouden: ten hoogste een verzoek per seconde, over de HELE
44 * applicatie. Geen bibliotheek en geen wachtrij -- een belofte die de volgende
45 * aanroeper laat wachten tot het weer mag. Zonder dit is de eerste drukke dag
46 * meteen een blokkade, en dan werkt het bij iedereen niet meer.
47 */
48let laatste = 0;
49let beurt = Promise.resolve();
50function opDeBeurt() {
51 beurt = beurt.then(async () => {
52 const wachten = 1000 - (Date.now() - laatste);
53 if (wachten > 0) await new Promise((r) => setTimeout(r, wachten));
54 laatste = Date.now();
55 });
56 return beurt;
57}
58
59/**
60 * Zoek artiesten op naam. Geeft de kandidaten met alles wat nodig is om er EEN
61 * uit te kiezen -- de naam alleen is niet genoeg, want er zijn drie bands die
62 * Nirvana heten. Vandaar disambiguation, land en de jaren erbij.
63 *
64 * Geeft een LEGE lijst bij een storing, geen exceptie: niet kunnen zoeken is
65 * vervelend, maar het mag het beheerscherm niet omvergooien.
66 */
67export async function zoekArtiesten(naam, { limit = 8 } = {}) {
68 const q = String(naam || '').trim();
69 if (!q) return [];
70 const url = `${BASIS}/artist?query=${encodeURIComponent(q)}&fmt=json&limit=${Math.min(25, Math.max(1, limit))}`;
71 try {
72 await opDeBeurt();
73 const r = await safeFetch(url, { headers: { Accept: 'application/json', 'User-Agent': userAgent() } });
74 if (!r || !r.ok) return [];
75 const doc = await r.json();
76 return (doc.artists || []).map(kandidaat).filter(Boolean);
77 } catch {
78 return [];
79 }
80}
81
82/** Een kandidaat, teruggebracht tot wat een mens nodig heeft om te kiezen. */
83function kandidaat(a) {
84 if (!a || !a.id || !a.name) return null;
85 const jaren = [a['life-span']?.begin, a['life-span']?.ended ? a['life-span']?.end : null]
86 .filter(Boolean).join(' – ');
87 return {
88 mbid: a.id,
89 naam: a.name,
90 // "disambiguation" is het veld waarmee MusicBrainz zelf twee gelijknamige
91 // artiesten uit elkaar houdt. Precies wat de kiezer nodig heeft.
92 toelichting: a.disambiguation || '',
93 soort: a.type || '', // Person, Group, ...
94 land: a.country || '',
95 jaren,
96 url: `https://musicbrainz.org/artist/${a.id}`,
97 // Hun eigen zoekscore. Niet om op te sorteren -- dat doen zij al -- maar om
98 // een zwakke treffer te kunnen tonen als zwak.
99 score: Number(a.score) || 0,
100 };
101}
102
[f59a46c]103// Her-geexporteerd zodat een aanroeper er niet over hoeft na te denken waar
104// ze precies wonen.
105export { isMbid, artiestUrl };
[a117862]106export default { zoekArtiesten, isMbid, artiestUrl };
Note: See TracBrowser for help on using the repository browser.