Changeset 013e16c in Klonkt
- Timestamp:
- 08/13/2026 01:09:24 PM (4 weeks ago)
- Branches:
- main
- Children:
- f50a84b
- Parents:
- f59a46c
- git-author:
- Robin <roboburr@…> (08/13/2026 01:01:18 PM)
- git-committer:
- Robin <roboburr@…> (08/13/2026 01:09:24 PM)
- Location:
- src
- Files:
-
- 1 added
- 7 edited
-
assets/js/mod/admin-seo.js (added)
-
assets/js/mod/admin-site-edit.js (modified) (1 diff)
-
routes/admin-seo.js (modified) (5 diffs)
-
routes/admin-sites.js (modified) (5 diffs)
-
services/i18n.js (modified) (3 diffs)
-
views/pages/admin-seo.ejs (modified) (1 diff)
-
views/pages/admin-site-edit.ejs (modified) (1 diff)
-
views/shell.ejs (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
src/assets/js/mod/admin-site-edit.js
rf59a46c r013e16c 135 135 })(); 136 136 137 // MusicBrainz-paneel (shaer-mbz). Binnen run(), niet op moduleniveau: deze138 // modules krijgen bij elke paginawissel opnieuw init(), en een blok dat maar139 // een keer per sessie draait is precies wat shaer-5s1 opleverde.140 wireMusicBrainz();141 137 } 142 143 // ── MusicBrainz: ben jij dit? (shaer-mbz) ─────────────────────────144 //145 // De kandidaten komen van de server, want MusicBrainz eist een verzoek per146 // seconde per APPLICATIE en een User-Agent met contact -- allebei niet vanuit147 // een browser af te dwingen.148 //149 // WIJ KIEZEN NIET. Ook niet als er precies een treffer is: een verkeerd geraden150 // MBID zet jouw naam onder andermans werk. De knop staat er, de klik is van de151 // artiest. En de keuze gaat mee met de Opslaan-knop van de pagina -- een eigen152 // formulier kan hier niet, want deze pagina IS er een.153 function wireMusicBrainz() {154 const knop = document.getElementById('mb-zoek-btn');155 const veld = document.getElementById('mb-q');156 const uit = document.getElementById('mb-uit');157 const idVeld = document.getElementById('mb-id');158 const naamVeld = document.getElementById('mb-naam');159 const huidig = document.getElementById('mb-huidig');160 if (!knop || !veld || !uit || !idVeld || knop.__wired) return;161 knop.__wired = true;162 163 const el = (tag, cls, tekst) => {164 const e = document.createElement(tag);165 if (cls) e.className = cls;166 // textContent en nooit innerHTML: dit is tekst uit een vreemd register.167 if (tekst != null) e.textContent = tekst;168 return e;169 };170 171 function zet(mbid, naam) {172 idVeld.value = mbid || '';173 naamVeld.value = naam || '';174 if (huidig) {175 huidig.hidden = !mbid;176 const n = document.getElementById('mb-huidig-naam');177 const a = document.getElementById('mb-huidig-link');178 if (n) n.textContent = naam || mbid || '';179 if (a) a.href = mbid ? `https://musicbrainz.org/artist/${mbid}` : '#';180 }181 uit.hidden = true;182 uit.replaceChildren();183 }184 185 const wis = document.getElementById('mb-wis');186 if (wis) wis.addEventListener('click', () => zet('', ''));187 188 async function zoek() {189 const q = (veld.value || '').trim();190 if (!q) return;191 uit.hidden = false;192 uit.replaceChildren(el('p', 'form-hint', T.asite_mb_busy || 'Zoeken…'));193 knop.disabled = true;194 try {195 const r = await fetch(`api/musicbrainz?q=${encodeURIComponent(q)}`, { credentials: 'same-origin' });196 const j = await r.json();197 toon((j && j.kandidaten) || []);198 } catch {199 uit.replaceChildren(el('p', 'form-hint', T.asite_mb_fail || 'MusicBrainz is even niet bereikbaar.'));200 } finally {201 knop.disabled = false;202 }203 }204 205 function toon(kandidaten) {206 if (!kandidaten.length) {207 uit.replaceChildren(el('p', 'form-hint', T.asite_mb_none || 'Niets gevonden.'));208 return;209 }210 const lijst = el('ul', 'mb-lijst');211 for (const k of kandidaten) {212 const li = el('li', 'mb-kandidaat');213 li.appendChild(el('strong', null, k.naam));214 // De toelichting is het hele punt: er zijn drie bands die Nirvana heten,215 // en zonder dit veld kiest iemand de verkeerde.216 const bij = [k.toelichting, k.soort, k.land, k.jaren].filter(Boolean).join(' · ');217 if (bij) li.appendChild(el('small', 'mb-bij', bij));218 const open = el('a', 'mb-open', T.asite_mb_open || 'Bekijk op MusicBrainz');219 open.href = k.url; open.target = '_blank'; open.rel = 'noopener';220 li.appendChild(open);221 const b = el('button', 'btn', T.asite_mb_pick || 'Dit ben ik');222 b.type = 'button';223 b.addEventListener('click', () => zet(k.mbid, k.naam));224 li.appendChild(b);225 lijst.appendChild(li);226 }227 uit.replaceChildren(lijst);228 }229 230 knop.addEventListener('click', zoek);231 veld.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); zoek(); } });232 } -
src/routes/admin-seo.js
rf59a46c r013e16c 19 19 import { requireGod } from '../middleware/auth.js'; 20 20 import { getPrimarySite } from '../middleware/site.js'; 21 import { isMbid } from '../services/ap-core.js'; 22 import MusicBrainz from '../services/MusicBrainzService.js'; 21 23 22 24 const router = express.Router(); … … 37 39 renderPage(req, res, 'pages/admin-seo', { 38 40 pageTitleKey: 'admin.t_seo', 41 pageJs: 'admin-seo', 39 42 bodyClass: 'on-admin', 40 43 site, … … 42 45 error: req.query.error || null, 43 46 }); 47 }); 48 49 /** 50 * "Zoek jezelf op" -- kandidaten uit MusicBrainz (shaer-mbz). 51 * 52 * De zoekopdracht draait HIER en niet in de browser: MusicBrainz staat een 53 * verzoek per seconde toe per APPLICATIE, en dat is alleen af te dwingen als 54 * alles langs een plek gaat. Bovendien eisen ze een User-Agent met contact, en 55 * die kan een browser niet zetten. 56 * 57 * Wij kiezen NIET. Ook niet als er precies een treffer is: een verkeerd geraden 58 * MBID zet jouw naam onder andermans werk. 59 */ 60 router.get('/api/musicbrainz', requireGod, async (req, res) => { 61 const site = getPrimarySite(req); 62 const q = String(req.query.q || (site && (site.publisher_name || site.title)) || '').trim(); 63 if (!q) return res.json({ ok: true, q: '', kandidaten: [] }); 64 res.json({ ok: true, q, kandidaten: await MusicBrainz.zoekArtiesten(q) }); 44 65 }); 45 66 … … 72 93 publisher_url = ?, 73 94 publisher_logo = ?, 95 mb_artist_id = ?, mb_artist_name = ?, 74 96 updated_at = CURRENT_TIMESTAMP 75 97 WHERE id = ? 98 // De MusicBrainz-koppeling (shaer-mbz). Alleen een echte MBID komt de kolom 99 // in: zonder deze zeef sluipt er een URL of een handle in het veld dat naar 100 // buiten gaat, en het gaat naar TWEE uitgangen -- de JSON-LD en de actor. 101 // Leeg is een geldige keuze; dat is ontkoppelen. 102 const mbRuw = String(f.mb_artist_id || '').trim().toLowerCase(); 103 const mbArtistId = isMbid(mbRuw) ? mbRuw : null; 104 76 105 `).run( 77 106 f.robots_index ? 1 : 0, … … 93 122 trimOrNull(f.publisher_url, 200), 94 123 trimOrNull(f.publisher_logo, 500), 124 mbArtistId, 125 mbArtistId ? (String(f.mb_artist_name || '').trim().slice(0, 200) || null) : null, 95 126 primary.id, 96 127 ); -
src/routes/admin-sites.js
rf59a46c r013e16c 27 27 import { mediaDir } from '../config/paths.js'; 28 28 import AP from '../services/ActivityPubService.js'; 29 import MusicBrainz from '../services/MusicBrainzService.js';30 29 31 30 … … 323 322 324 323 // ==================== SAVE ==================== 325 /**326 * "Ben jij dit?" -- kandidaten uit MusicBrainz (shaer-mbz).327 *328 * De zoekopdracht draait HIER en niet in de browser: MusicBrainz staat een329 * verzoek per seconde toe per APPLICATIE, en dat is alleen af te dwingen als330 * alles langs een plek gaat. Bovendien eisen ze een User-Agent met contact, en331 * die kan een browser niet zetten.332 *333 * Wij kiezen NIET. Ook niet als er precies een treffer is: een verkeerd geraden334 * MBID zet jouw naam onder andermans werk.335 */336 router.get('/:slug/api/musicbrainz', requireSiteManagerBySlug, async (req, res) => {337 const site = db.prepare('SELECT title, mb_artist_id, mb_artist_name FROM sites WHERE slug = ?').get(req.params.slug);338 if (!site) return res.status(404).json({ error: 'no_site' });339 const q = String(req.query.q || site.title || '').trim();340 if (!q) return res.json({ ok: true, q: '', kandidaten: [] });341 res.json({ ok: true, q, kandidaten: await MusicBrainz.zoekArtiesten(q) });342 });343 344 324 router.post('/:slug/save', requireSiteManagerBySlug, async (req, res) => { 345 325 const site = db.prepare('SELECT id, ap_aliases FROM sites WHERE slug = ?').get(req.params.slug); … … 366 346 // accent: only accept colors from the curated ACCENTS list. Falls back to 367 347 // the orange default if the submitted value isn't recognised. 368 // De MusicBrainz-koppeling (shaer-mbz). Alleen een echte MBID komt de kolom369 // in: zonder deze zeef sluipt er een hele URL of een handle in het veld dat370 // straks naar buiten gaat. Leeg is een geldige keuze -- dat is ontkoppelen.371 const mbRuw = String(f.mb_artist_id || '').trim().toLowerCase();372 const mbArtistId = MusicBrainz.isMbid(mbRuw) ? mbRuw : null;373 374 348 const accent = ThemeService.validateAccent(f.accent) || '#e8b04b'; 375 349 … … 381 355 profile_links = ?, 382 356 ap_aliases = ?, 383 mb_artist_id = ?, mb_artist_name = ?,384 357 is_public = ?, robots_index = ?, require_login_to_comment = ?, 385 358 enable_audio_player = ?, … … 401 374 profileLinksJson, 402 375 apAliasesJson, 403 mbArtistId,404 mbArtistId ? (String(f.mb_artist_name || '').trim().slice(0, 200) || null) : null,405 376 f.is_public ? 1 : 0, 406 377 f.robots_index ? 1 : 0, -
src/services/i18n.js
rf59a46c r013e16c 360 360 'aaud.fedi_off': 'Niet op de fediverse gedeeld (alleen webspeler, bestand verborgen) — klik om te delen', 361 361 'aaud.embed_player': 'Embedbare speler', 362 'asite.mb_title': 'Ben jij dit in MusicBrainz?', 363 'asite.mb_hint': 'MusicBrainz is het open register waar muziekdiensten je aan herkennen. Zoek jezelf op en kies je eigen pagina — dan weten anderen dat jouw muziek van jou is.', 364 'asite.mb_search': 'Zoeken', 365 'asite.mb_placeholder': 'Je artiestennaam', 366 'asite.mb_none': 'Niets gevonden. Sta je er nog niet in? Dan kun je jezelf aanmelden op musicbrainz.org — dat kan alleen daar, niet vanuit Klonkt.', 367 'asite.mb_pick': 'Dit ben ik', 368 'asite.mb_linked': 'Gekoppeld aan', 369 'asite.mb_unlink': 'Ontkoppelen', 370 'asite.mb_open': 'Bekijk op MusicBrainz', 371 'asite.mb_busy': 'Zoeken…', 372 'asite.mb_fail': 'MusicBrainz is even niet bereikbaar.', 362 'aseo.mb_legend': 'MusicBrainz (dezelfde artiest elders)', 363 'aseo.mb_search_label': 'Zoek jezelf op', 364 'aseo.mb_search_hint': 'de naam waaronder je muziek uitbrengt', 365 'aseo.mb_hint': 'MusicBrainz is het open register van de muziekwereld. Koppel je eigen pagina en je komt als sameAs in de JSON-LD van deze site én in je fediverse-profiel te staan — zo weten zoekmachines en muziekdiensten dat jouw muziek van jou is. Je verandert er niets mee bij MusicBrainz zelf; we verwijzen er alleen naar.', 366 'aseo.mb_search': 'Zoeken', 367 'aseo.mb_placeholder': 'Je artiestennaam', 368 'aseo.mb_none': 'Niets gevonden. Sta je er nog niet in? Dan kun je jezelf aanmelden op musicbrainz.org — dat kan alleen daar, niet vanuit Klonkt.', 369 'aseo.mb_pick': 'Dit ben ik', 370 'aseo.mb_linked': 'Gekoppeld aan', 371 'aseo.mb_unlink': 'Ontkoppelen', 372 'aseo.mb_open': 'Bekijk op MusicBrainz', 373 'aseo.mb_busy': 'Zoeken…', 374 'aseo.mb_fail': 'MusicBrainz is even niet bereikbaar.', 373 375 'aaud.embed_hint': 'Plak deze code op je eigen website/blog om je muziek met deze speler in te sluiten:', 374 376 'aaud.preview_player': 'Speler-voorbeeld openen', … … 1318 1320 'aaud.fedi_off': 'Not shared on the fediverse (web player only, file hidden) — click to share', 1319 1321 'aaud.embed_player': 'Embeddable player', 1320 'asite.mb_title': 'Is this you on MusicBrainz?', 1321 'asite.mb_hint': 'MusicBrainz is the open register music services recognise you by. Look yourself up and pick your own page — then others know your music is yours.', 1322 'asite.mb_search': 'Search', 1323 'asite.mb_placeholder': 'Your artist name', 1324 'asite.mb_none': 'Nothing found. Not in there yet? You can add yourself on musicbrainz.org — that can only be done there, not from Klonkt.', 1325 'asite.mb_pick': 'This is me', 1326 'asite.mb_linked': 'Linked to', 1327 'asite.mb_unlink': 'Unlink', 1328 'asite.mb_open': 'View on MusicBrainz', 1329 'asite.mb_busy': 'Searching…', 1330 'asite.mb_fail': 'MusicBrainz is unreachable right now.', 1322 'aseo.mb_legend': 'MusicBrainz (the same artist elsewhere)', 1323 'aseo.mb_search_label': 'Look yourself up', 1324 'aseo.mb_search_hint': 'the name you release music under', 1325 'aseo.mb_hint': 'MusicBrainz is the music world\u2019s open register. Link your own page and it appears as sameAs in this site\u2019s JSON-LD and in your fediverse profile — so search engines and music services know your music is yours. It changes nothing at MusicBrainz itself; we only point at it.', 1326 'aseo.mb_search': 'Search', 1327 'aseo.mb_placeholder': 'Your artist name', 1328 'aseo.mb_none': 'Nothing found. Not in there yet? You can add yourself on musicbrainz.org — that can only be done there, not from Klonkt.', 1329 'aseo.mb_pick': 'This is me', 1330 'aseo.mb_linked': 'Linked to', 1331 'aseo.mb_unlink': 'Unlink', 1332 'aseo.mb_open': 'View on MusicBrainz', 1333 'aseo.mb_busy': 'Searching…', 1334 'aseo.mb_fail': 'MusicBrainz is unreachable right now.', 1331 1335 'aaud.embed_hint': 'Paste this code on your own website/blog to embed your music with this player:', 1332 1336 'aaud.preview_player': 'Open player preview', … … 2275 2279 'aaud.fedi_off': 'Nicht im Fediverse geteilt (nur Web-Player, Datei verborgen) — zum Teilen klicken', 2276 2280 'aaud.embed_player': 'Einbettbarer Player', 2277 'asite.mb_title': 'Bist das du bei MusicBrainz?', 2278 'asite.mb_hint': 'MusicBrainz ist das offene Register, an dem Musikdienste dich erkennen. Suche dich und wähle deine eigene Seite — dann wissen andere, dass deine Musik von dir ist.', 2279 'asite.mb_search': 'Suchen', 2280 'asite.mb_placeholder': 'Dein Künstlername', 2281 'asite.mb_none': 'Nichts gefunden. Noch nicht dabei? Du kannst dich auf musicbrainz.org eintragen — das geht nur dort, nicht aus Klonkt heraus.', 2282 'asite.mb_pick': 'Das bin ich', 2283 'asite.mb_linked': 'Verknüpft mit', 2284 'asite.mb_unlink': 'Trennen', 2285 'asite.mb_open': 'Bei MusicBrainz ansehen', 2286 'asite.mb_busy': 'Suche läuft…', 2287 'asite.mb_fail': 'MusicBrainz ist gerade nicht erreichbar.', 2281 'aseo.mb_legend': 'MusicBrainz (dieselbe Künstlerin anderswo)', 2282 'aseo.mb_search_label': 'Dich selbst suchen', 2283 'aseo.mb_search_hint': 'der Name, unter dem du Musik veröffentlichst', 2284 'aseo.mb_hint': 'MusicBrainz ist das offene Register der Musikwelt. Verknüpfe deine Seite, und sie erscheint als sameAs im JSON-LD dieser Website und in deinem Fediverse-Profil — so wissen Suchmaschinen und Musikdienste, dass deine Musik von dir ist. Bei MusicBrainz selbst ändert sich nichts; wir verweisen nur darauf.', 2285 'aseo.mb_search': 'Suchen', 2286 'aseo.mb_placeholder': 'Dein Künstlername', 2287 'aseo.mb_none': 'Nichts gefunden. Noch nicht dabei? Du kannst dich auf musicbrainz.org eintragen — das geht nur dort, nicht aus Klonkt heraus.', 2288 'aseo.mb_pick': 'Das bin ich', 2289 'aseo.mb_linked': 'Verknüpft mit', 2290 'aseo.mb_unlink': 'Trennen', 2291 'aseo.mb_open': 'Bei MusicBrainz ansehen', 2292 'aseo.mb_busy': 'Suche läuft…', 2293 'aseo.mb_fail': 'MusicBrainz ist gerade nicht erreichbar.', 2288 2294 'aaud.embed_hint': 'Füge diesen Code auf deiner eigenen Website/deinem Blog ein, um deine Musik mit diesem Player einzubetten:', 2289 2295 'aaud.preview_player': 'Player-Vorschau öffnen', -
src/views/pages/admin-seo.ejs
rf59a46c r013e16c 88 88 </fieldset> 89 89 90 <%# ── MUSICBRAINZ ───────────────────────────────────────────── 91 Direct onder Uitgever, en dat is geen willekeur: sameAs IS een 92 schema.org-eigenschap, en dat blok hierboven is waar ons schema.org 93 vandaan komt. Een koppeling hier voedt twee uitgangen tegelijk -- de 94 JSON-LD van de pagina en het fediverse-profiel (shaer-mbz). %> 95 <fieldset id="mb-veld"> 96 <legend><%= t('aseo.mb_legend') %></legend> 97 <p class="form-hint"><%= t('aseo.mb_hint') %></p> 98 99 <input type="hidden" name="mb_artist_id" id="mb-id" value="<%= site.mb_artist_id || '' %>"> 100 <input type="hidden" name="mb_artist_name" id="mb-naam" value="<%= site.mb_artist_name || '' %>"> 101 102 <p class="mb-huidig" id="mb-huidig" <%= site.mb_artist_id ? '' : 'hidden' %>> 103 <%= t('aseo.mb_linked') %> 104 <strong id="mb-huidig-naam"><%= site.mb_artist_name || site.mb_artist_id || '' %></strong> 105 <a id="mb-huidig-link" target="_blank" rel="noopener" 106 href="https://musicbrainz.org/artist/<%= site.mb_artist_id || '' %>"><%= t('aseo.mb_open') %></a> 107 <button type="button" class="btn" id="mb-wis"><%= t('aseo.mb_unlink') %></button> 108 </p> 109 110 <label> 111 <span><%= t('aseo.mb_search_label') %> <small class="form-hint-inline">— <%= t('aseo.mb_search_hint') %></small></span> 112 <div class="mb-zoek"> 113 <input type="text" id="mb-q" value="<%= site.publisher_name || site.title || '' %>" 114 autocomplete="off" placeholder="<%= t('aseo.mb_placeholder') %>"> 115 <button type="button" class="btn" id="mb-zoek-btn"><%= t('aseo.mb_search') %></button> 116 </div> 117 </label> 118 <div id="mb-uit" class="mb-uit" hidden></div> 119 </fieldset> 120 90 121 <fieldset> 91 122 <legend><%= t('aseo.verify_legend') %></legend> -
src/views/pages/admin-site-edit.ejs
rf59a46c r013e16c 190 190 </fieldset> 191 191 192 <%# ── MUSICBRAINZ ─────────────────────────────────────────────193 Naast de aliassen, want het is dezelfde vraag: wie ben jij elders?194 Een MBID is geen vocabulaire maar een register -- eraan koppelen is als195 je ISBN noemen, en muziekdiensten herkennen je eraan zonder dat wij hun196 model hoeven over te nemen (shaer-mbz).197 198 GEEN EIGEN FORMULIER: deze pagina IS er een, en een formulier in een199 formulier bestaat niet in HTML. De keuze zet een verborgen veld en gaat200 mee met de Opslaan-knop van de pagina, net als de profielfoto. %>201 <fieldset id="mb-veld">202 <legend><%= t('asite.mb_title') %></legend>203 <p class="form-hint"><%= t('asite.mb_hint') %></p>204 205 <input type="hidden" name="mb_artist_id" id="mb-id" value="<%= site.mb_artist_id || '' %>">206 <input type="hidden" name="mb_artist_name" id="mb-naam" value="<%= site.mb_artist_name || '' %>">207 208 <p class="mb-huidig" id="mb-huidig" <%= site.mb_artist_id ? '' : 'hidden' %>>209 <%= t('asite.mb_linked') %>210 <strong id="mb-huidig-naam"><%= site.mb_artist_name || site.mb_artist_id || '' %></strong>211 <a id="mb-huidig-link" target="_blank" rel="noopener"212 href="https://musicbrainz.org/artist/<%= site.mb_artist_id || '' %>"><%= t('asite.mb_open') %></a>213 <button type="button" class="btn" id="mb-wis"><%= t('asite.mb_unlink') %></button>214 </p>215 216 <div class="mb-zoek">217 <input type="text" id="mb-q" value="<%= site.title || '' %>" autocomplete="off"218 placeholder="<%= t('asite.mb_placeholder') %>">219 <button type="button" class="btn" id="mb-zoek-btn"><%= t('asite.mb_search') %></button>220 </div>221 <div id="mb-uit" class="mb-uit" hidden></div>222 </fieldset>223 224 192 <fieldset> 225 193 <legend><%= t('asite.feed_view') %></legend> -
src/views/shell.ejs
rf59a46c r013e16c 90 90 if (safeSite.publisher_logo) { 91 91 _publisher.logo = { '@type': 'ImageObject', url: safeSite.publisher_logo }; 92 } 93 // Dezelfde koppeling die de fediverse-actor draagt, hier in de JSON-LD 94 // (shaer-mbz). sameAs is schema.org-eigen, dus dit is geen extra vocabulaire -- 95 // het is hetzelfde feit, verteld aan de andere lezer. 96 if (safeSite.mb_artist_id) { 97 _publisher.sameAs = 'https://musicbrainz.org/artist/' + safeSite.mb_artist_id; 92 98 } 93 99 let _jsonLd = null;
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)