Changeset c832064 in Klonkt
- Timestamp:
- 08/09/2026 07:37:31 AM (4 weeks ago)
- Branches:
- main
- Children:
- d7e72b8
- Parents:
- e667d26
- Files:
-
- 2 edited
-
src/services/music/index.js (modified) (3 diffs)
-
test/music-type.test.js (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/music/index.js
re667d26 rc832064 336 336 337 337 /** 338 * Het type van een post afleiden uit de muziek die erin staat. 338 * Het type van een BESTAANDE post afleiden uit de muziek die erin staat. 339 * 340 * WAARVOOR DIT WEL EN NIET IS (Robins afbakening, 9-8). Nieuwe posts krijgen 341 * hun type uit de keuze: album of playlist wordt gekozen als de playlist wordt 342 * gemaakt, en de post neemt dat over. Deze functie is er voor wat er al staat -- 343 * de posts met type=audio uit de tijd voor die keuze bestond. Daarmee is de 344 * vraag "wie wint, de keuze of de afleiding?" geen vraag meer: ze komen elkaar 345 * niet tegen. De afleiding draait eenmalig, de keuze draait daarna. 339 346 * 340 347 * DE REGEL GAAT OVER IDENTITEIT, NIET OVER TELLEN (Robins herformulering, 9-8): … … 357 364 * letterlijk een album; hem als losse tracks tellen zou een albumpost tot 358 365 * playlist maken. Telt hij straks anders, dan is dat hier een regel. 359 */ 360 export function postMusicType(content) { 366 * 367 * WIE KIEST ALBUM OF PLAYLIST: dat gebeurt wanneer de PLAYLIST wordt gemaakt, 368 * en die keuze staat al in playlists.kind. Bij een enkele insluiting neemt de 369 * post dus die soort over -- een post om een album is een album, ook al heet de 370 * shortcode [[playlist:...]]. De soort van een playlist wordt hier dus 371 * OPGEZOCHT en niet afgeleid. 372 * 373 * EN ALS WE HET NIET WETEN: een gewone post met insluitingen die de post als 374 * context hebben. Dat is geen noodgreep maar de rustende toestand -- tracks 375 * wijzen met `context` toch al terug naar hun post, dus er gaat niets verloren 376 * als het label 'post' wordt. 377 * 378 * @param {string} content de HTML/tekst van de post 379 * @param {string} siteId nodig om playlists.kind te kunnen opzoeken 380 */ 381 export function postMusicType(content, siteId) { 361 382 const c = String(content || ''); 362 383 const uniek = (re) => [...new Set([...c.matchAll(re)].map((m) => m[1].trim()))]; 363 384 364 const playlists = uniek(/\[\[playlist:([A-Za-z0-9_-]+)\]\]/g); 385 // Dezelfde patronen als de renderer in AudioEmbedService: wat daar niet 386 // insluit, telt hier niet mee. Anders zou een shortcode die niets oplevert 387 // wel het type van de post kunnen bepalen. 388 const playlists = uniek(/\[\[playlist:([a-z0-9][a-z0-9-]*)\]\]/gi); 365 389 const albums = uniek(/\[\[album:([^\]]+)\]\]/g); 366 390 const tracks = uniek(/\[\[track:([A-Za-z0-9_-]+)\]\]/g); 367 391 392 if (!playlists.length && !albums.length && !tracks.length) return null; 393 394 // De soort van een playlist is een gegeven, geen afleiding. 395 let onbekend = []; 396 const uitPlaylists = playlists.map((id) => { 397 const kind = playlistKind(id, siteId); 398 if (!kind) { onbekend.push(id); return null; } 399 return { soort: kind, id }; 400 }).filter(Boolean); 401 368 402 const collecties = [ 369 ... playlists.map((id) => ({ soort: 'playlist', id })),403 ...uitPlaylists, 370 404 ...albums.map((naam) => ({ soort: 'album', naam })), 371 405 ]; 372 406 373 if (!collecties.length && !tracks.length) return null; // geen muziek 407 // Onbekende situatie -> gewone post. De insluitingen blijven staan en houden 408 // de post als context; alleen het label wordt niet verzonnen. 409 if (onbekend.length) { 410 return { type: 'post', collecties, tracks, bonus: [], onbekend, leentMetadata: false }; 411 } 374 412 375 413 if (!collecties.length) { … … 389 427 return { type: 'post', collecties, tracks, bonus: [], leentMetadata: false }; 390 428 } 429 430 /** 431 * De gekozen soort van een playlist: 'album' | 'playlist', of null als hij niet 432 * (op deze site) bestaat. Zelfde normalisatie als PlaylistService: alles wat 433 * geen 'playlist' zegt is een album. 434 */ 435 function playlistKind(id, siteId) { 436 if (!siteId) return null; 437 try { 438 const r = db.prepare('SELECT kind FROM playlists WHERE id = ? AND site_id = ?').get(id, siteId); 439 if (!r) return null; 440 return r.kind === 'playlist' ? 'playlist' : 'album'; 441 } catch { return null; } 442 } -
test/music-type.test.js
re667d26 rc832064 1 // Welk soort uitgave is een post? (shaer-cyg)1 // Welk soort uitgave is een BESTAANDE post? (shaer-cyg) 2 2 // 3 // De regel gaat over IDENTITEIT en niet over tellen: een post neemt het type 4 // van zijn muziek over als hij precies EEN muzikale eenheid bevat. Zijn het er 5 // meer, dan is de post een post die naar muziek verwijst en houden de 6 // collecties hun eigen identiteit -- en vervalt de reden om de posttitel en 7 // -cover te lenen. 3 // Nieuwe posts krijgen hun type uit de keuze -- album of playlist wordt gekozen 4 // als de playlist wordt gemaakt. Deze afleiding is er voor wat er al staat: de 5 // type=audio posts van voor die keuze. De regel gaat over IDENTITEIT en niet 6 // over tellen: een post neemt het type van zijn muziek over als hij precies EEN 7 // muzikale eenheid bevat. Zijn het er meer, dan is de post een post die naar 8 // muziek verwijst en houden de collecties hun eigen identiteit -- en vervalt de 9 // reden om de posttitel en -cover te lenen. 8 10 import { test } from 'node:test'; 9 11 import assert from 'node:assert/strict'; … … 14 16 const dbMod = await import('../src/config/database.js'); 15 17 dbMod.initializeDatabase(); 18 const db = dbMod.default; 16 19 const { postMusicType } = await import('../src/services/music/index.js'); 17 20 21 const SITE = 'site-muziek'; 22 const ANDERE = 'site-anders'; 23 db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)') 24 .run('u1', 'robin', 'r@test', 'x', 'god'); 25 db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run(SITE, 'muziek', 'Muziek', 'u1'); 26 db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run(ANDERE, 'anders', 'Anders', 'u1'); 27 28 const maakPlaylist = (id, kind, siteId = SITE) => 29 db.prepare('INSERT INTO playlists (id, site_id, title, kind) VALUES (?, ?, ?, ?)') 30 .run(id, siteId, id, kind); 31 32 maakPlaylist('nachtlicht', 'playlist'); 33 maakPlaylist('de-plaat', 'album'); 34 maakPlaylist('tweede', 'playlist'); 35 maakPlaylist('elders', 'playlist', ANDERE); 36 37 const type = (c) => postMusicType(c, SITE); 38 18 39 test('geen muziek: geen type', () => { 19 assert.equal( postMusicType('<p>gewoon een bericht</p>'), null);20 assert.equal( postMusicType(''), null);21 assert.equal( postMusicType(null), null);40 assert.equal(type('<p>gewoon een bericht</p>'), null); 41 assert.equal(type(''), null); 42 assert.equal(type(null), null); 22 43 }); 23 44 24 45 test('losse tracks worden een playlist, en lenen de metadata van de post', () => { 25 const r = postMusicType('Twee nieuwe: [[track:a]] en [[track:b]]');46 const r = type('Twee nieuwe: [[track:a]] en [[track:b]]'); 26 47 assert.equal(r.type, 'playlist'); 27 48 assert.deepEqual(r.tracks, ['a', 'b']); … … 32 53 // Naar buiten toe is er dan altijd een collectie om naar te wijzen. Dat 33 54 // "Playlist" boven een enkel nummer gek leest is een weergavekwestie. 34 assert.equal( postMusicType('[[track:a]]').type, 'playlist');55 assert.equal(type('[[track:a]]').type, 'playlist'); 35 56 }); 36 57 37 test('een playlist blijft een playlist', () => { 38 const r = postMusicType('Nieuw: [[playlist:nachtlicht]]'); 39 assert.equal(r.type, 'playlist'); 58 test('de post neemt de GEKOZEN soort van de playlist over', () => { 59 // De kern van Robins besluit: album of playlist is bij het maken van de 60 // playlist al gekozen. Hier wordt hij opgezocht, niet afgeleid. 61 assert.equal(type('Nieuw: [[playlist:nachtlicht]]').type, 'playlist'); 62 assert.equal(type('Nieuw: [[playlist:de-plaat]]').type, 'album', 63 'een post om een album is een album, ook al heet de shortcode playlist'); 64 }); 65 66 test('de collectie draagt haar eigen soort mee', () => { 67 const r = type('Nieuw: [[playlist:nachtlicht]]'); 40 68 assert.deepEqual(r.collectie, { soort: 'playlist', id: 'nachtlicht' }); 41 69 assert.equal(r.leentMetadata, true); … … 43 71 44 72 test('een album-insluiting is ook een collectie, en telt als album', () => { 45 // Robins regel noemde alleen playlists; [[album:naam]] groepeert net zo goed 46 // tracks. Hem als losse tracks tellen zou een albumpost tot playlist maken. 47 const r = postMusicType('Luister [[album:De Plaat]]'); 73 // [[album:naam]] groepeert tracks op hun albumveld -- geen playlist-rij, dus 74 // geen gekozen soort. Hem als losse tracks tellen zou een albumpost tot 75 // playlist maken. 76 const r = type('Luister [[album:De Plaat]]'); 48 77 assert.equal(r.type, 'album'); 49 78 assert.deepEqual(r.collectie, { soort: 'album', naam: 'De Plaat' }); … … 51 80 52 81 test('een collectie MET losse tracks is een album, en die losse zijn bonus', () => { 53 const r = postMusicType('[[playlist:nachtlicht]] plus [[track:x]] en [[track:y]]');54 assert.equal(r.type, 'album' );82 const r = type('[[playlist:nachtlicht]] plus [[track:x]] en [[track:y]]'); 83 assert.equal(r.type, 'album', 'ook al is de playlist zelf een playlist'); 55 84 assert.equal(r.collectie.id, 'nachtlicht'); 56 85 assert.deepEqual(r.bonus, ['x', 'y'], 'geen rommelrestje maar bonus-tracks'); … … 60 89 61 90 test('TWEE collecties: de post blijft een post en leent niets', () => { 62 const r = postMusicType('[[playlist:een]] en ook [[playlist:twee]]');91 const r = type('[[playlist:nachtlicht]] en ook [[playlist:tweede]]'); 63 92 assert.equal(r.type, 'post'); 64 93 assert.equal(r.collecties.length, 2); … … 68 97 69 98 test('twee collecties van verschillende soort tellen ook als twee', () => { 70 const r = postMusicType('[[playlist:een]] naast [[album:De Plaat]]');99 const r = type('[[playlist:nachtlicht]] naast [[album:De Plaat]]'); 71 100 assert.equal(r.type, 'post'); 72 101 assert.equal(r.collecties.length, 2); … … 76 105 // Anders maakt een herhaalde shortcode van een albumpost stilletjes een 77 106 // gewone post. 78 const r = postMusicType('[[playlist:een]] ... en nog eens [[playlist:een]]');107 const r = type('[[playlist:nachtlicht]] ... en nog eens [[playlist:nachtlicht]]'); 79 108 assert.equal(r.type, 'playlist'); 80 assert.equal(r.collectie.id, ' een');109 assert.equal(r.collectie.id, 'nachtlicht'); 81 110 }); 82 111 83 112 test('dezelfde track twee keer telt ook maar een keer', () => { 84 assert.deepEqual( postMusicType('[[track:a]] [[track:a]] [[track:b]]').tracks, ['a', 'b']);113 assert.deepEqual(type('[[track:a]] [[track:a]] [[track:b]]').tracks, ['a', 'b']); 85 114 }); 115 116 test('onbekende playlist: gewone post, niets verzonnen', () => { 117 // Robins laatste optie voor een onbekende situatie. De insluiting blijft 118 // staan en houdt de post als context; alleen het label wordt niet geraden. 119 const r = type('[[playlist:bestaat-niet]]'); 120 assert.equal(r.type, 'post'); 121 assert.deepEqual(r.onbekend, ['bestaat-niet']); 122 assert.equal(r.leentMetadata, false); 123 }); 124 125 test('een playlist van een ANDERE site telt niet als de onze', () => { 126 const r = type('[[playlist:elders]]'); 127 assert.equal(r.type, 'post'); 128 assert.deepEqual(r.onbekend, ['elders']); 129 }); 130 131 test('zonder site is er niets op te zoeken, dus geen type verzinnen', () => { 132 assert.equal(postMusicType('[[playlist:nachtlicht]]').type, 'post'); 133 });
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)