Changeset b406d25 in Klonkt
- Timestamp:
- 08/09/2026 07:57:50 AM (4 weeks ago)
- Branches:
- main
- Children:
- b2adc20
- Parents:
- d7e72b8
- Location:
- src
- Files:
-
- 1 added
- 2 edited
-
assets/js/mod/post-edit.js (modified) (4 diffs)
-
assets/js/shared/post-music-type.js (added)
-
services/music/index.js (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/assets/js/mod/post-edit.js
rd7e72b8 rb406d25 6 6 7 7 import { pageData, esc, makeSweeper } from './lib.js'; 8 // Dezelfde regel die de server gebruikt -- zie shared/post-music-type.js voor 9 // waarom hij daar staat en niet twee keer. 10 import { afleidenUitInsluitingen } from '../shared/post-music-type.js'; 8 11 9 12 // De oude inline scripts draaiden bij ELKE render; een module draait zijn … … 791 794 } 792 795 updateCharCount(); 793 } 796 volgMuziek(); 797 } 798 799 // Wordt hieronder gevuld door het typeblok. Zolang dat er niet is (of de 800 // gebruiker het type zelf koos) gebeurt er niets -- insertChip mag daar niet 801 // op stuklopen. 802 let volgMuziek = () => {}; 794 803 795 804 // ── Embed insert: paste a platform URL -> [[embed:url]]-chip that becomes … … 972 981 const seg = card.querySelector('.pe-typeseg'); 973 982 const panels = card.querySelectorAll('.pe-type-panel'); 983 let handmatig = false; // heeft de gebruiker het type zelf aangeklikt? 974 984 975 985 function applyType(tt) { … … 989 999 seg.addEventListener('click', (e) => { 990 1000 const btn = e.target.closest('.pe-typeseg-btn'); 991 if (btn) applyType(btn.dataset.type); 1001 if (!btn) return; 1002 handmatig = true; // jouw klik wint vanaf nu van de afleiding 1003 applyType(btn.dataset.type); 992 1004 }); 993 1005 applyType(typeInput.value || 'post'); 1006 1007 // ── Het type volgt de muziek (shaer-cyg) ────────────────────── 1008 // 1009 // Robins regel: de post neemt de soort van zijn muziek over als hij precies 1010 // EEN muzikale eenheid bevat. Dus zodra je een playlist invoegt verspringt 1011 // de balk mee, en zie je wat je aan het maken bent in plaats van het zelf te 1012 // moeten bijhouden. 1013 // 1014 // TWEE DINGEN DIE HIJ MET RUST LAAT. Klik je zelf een type aan, dan wint die 1015 // keuze -- daarna wordt er niets meer voor je omgezet. En Foto en Video zijn 1016 // een bewuste andere keuze over dezelfde post, dus die overschrijft hij 1017 // nooit, ook niet als er muziek in staat. 1018 // 1019 // Bij het OPENEN van een post gebeurt er niets: de eerste afleiding wordt 1020 // alleen als ijkpunt onthouden. Anders zou een oude post van type veranderen 1021 // door hem te bekijken. 1022 const VOLGBAAR = new Set(['post', 'album', 'playlist', 'audio']); 1023 const kindVan = new Map(); 1024 let vorigeAfleiding = null; 1025 1026 const soortenGeladen = fetch('/admin/playlists/api/list', { credentials: 'same-origin' }) 1027 .then((r) => r.json()) 1028 .then((j) => { 1029 if (j && j.ok && Array.isArray(j.playlists)) { 1030 for (const p of j.playlists) kindVan.set(p.id, p.kind === 'playlist' ? 'playlist' : 'album'); 1031 } 1032 }) 1033 // Zonder de soorten leidt de regel niets af (elke playlist is dan 1034 // 'onbekend'), en dat is beter dan een gok die als keuze oogt. 1035 .catch(() => {}) 1036 .then(() => { vorigeAfleiding = afleidenNu(); }); 1037 1038 function afleidenNu() { 1039 // Precies de tekst die straks wordt opgeslagen: chips terug naar 1040 // shortcodes. Zo leidt de editor af uit wat de server ook zal zien. 1041 const clone = editor.cloneNode(true); 1042 serializeChips(clone); 1043 const r = afleidenUitInsluitingen(clone.innerHTML, (id) => kindVan.get(id) || null); 1044 return r ? r.type : null; 1045 } 1046 1047 volgMuziek = async () => { 1048 await soortenGeladen; 1049 const nu = afleidenNu(); 1050 if (nu === vorigeAfleiding) return; // de muziek is niet van soort veranderd 1051 vorigeAfleiding = nu; 1052 if (handmatig || !nu) return; 1053 if (!VOLGBAAR.has(typeInput.value)) return; 1054 applyType(nu); 1055 }; 1056 1057 // Ook getypte of geplakte shortcodes tellen, niet alleen de knoppen. 1058 let tik = null; 1059 editor.addEventListener('input', () => { 1060 clearTimeout(tik); 1061 tik = setTimeout(volgMuziek, 300); 1062 }); 994 1063 995 1064 // Video URL → [[embed:url]] chip -
src/services/music/index.js
rd7e72b8 rb406d25 15 15 import db from '../../config/database.js'; 16 16 import { AP_CONTEXT, PUBLIC, actorId, noteId, safeUrl, guessMediaType } from '../ap-core.js'; 17 import { afleidenUitInsluitingen } from '../../assets/js/shared/post-music-type.js'; 17 18 18 19 // m.size hoort erbij voor de RSS-enclosure: die eist een lengte in bytes. … … 333 334 } 334 335 335 // ── Welk soort muzikale uitgave is deze post? (shaer-cyg) ───────────────── 336 337 /** 338 * Het type van een BESTAANDE post afleiden uit de muziek die erin staat. 336 // ── Welk soort muzikale uitgave is deze post? (shaer-cyg) ───────────── 337 338 /** 339 * Het type van een post afleiden uit de muziek die erin staat. 340 * 341 * DE REGEL ZELF staat in assets/js/shared/post-music-type.js, want de editor 342 * gebruikt hem ook -- daar volgt het type live mee terwijl je schrijft. Twee 343 * kopieen zouden stil uit elkaar lopen, dus is er er een. Hier komt alleen het 344 * stuk bij dat de server kan en de browser niet: de gekozen soort van een 345 * playlist opzoeken. 339 346 * 340 347 * WAARVOOR DIT WEL EN NIET IS (Robins afbakening, 9-8). Nieuwe posts krijgen 341 348 * 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. 346 * 347 * DE REGEL GAAT OVER IDENTITEIT, NIET OVER TELLEN (Robins herformulering, 9-8): 348 * een post neemt het type van zijn muziek over als hij precies EEN muzikale 349 * eenheid bevat. Zijn het er meer, dan is de post een post die naar muziek 350 * verwijst, en houden de collecties hun eigen identiteit. 351 * 352 * losse track(s) -> playlist metadata van de post geleend 353 * een collectie -> die soort metadata van de post geleend 354 * een collectie + losse tracks -> album de losse zijn BONUS-TRACKS 355 * twee of meer collecties -> post NIETS geleend 356 * 357 * Waarom de lening bij de laatste vervalt: die bestaat omdat een collectie soms 358 * dun is -- geen eigen hoes, geen eigen titel. Bij twee is de post niet meer de 359 * drager van EEN identiteit, en vervalt de reden vanzelf. Dezelfde regel zet 360 * zichzelf uit. 361 * 362 * EEN COLLECTIE IS EEN PLAYLIST OF EEN ALBUM-INSLUITING. Robins regel noemde 363 * alleen playlists, maar [[album:naam]] groepeert net zo goed tracks en is 364 * letterlijk een album; hem als losse tracks tellen zou een albumpost tot 365 * playlist maken. Telt hij straks anders, dan is dat hier een regel. 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. 349 * gemaakt, en de post neemt dat over. Op de server is dit vooral voor wat er al 350 * staat -- de posts met type=audio uit de tijd voor die keuze bestond. 377 351 * 378 352 * @param {string} content de HTML/tekst van de post … … 380 354 */ 381 355 export function postMusicType(content, siteId) { 382 const c = String(content || ''); 383 const uniek = (re) => [...new Set([...c.matchAll(re)].map((m) => m[1].trim()))]; 384 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); 389 const albums = uniek(/\[\[album:([^\]]+)\]\]/g); 390 const tracks = uniek(/\[\[track:([A-Za-z0-9_-]+)\]\]/g); 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 402 const collecties = [ 403 ...uitPlaylists, 404 ...albums.map((naam) => ({ soort: 'album', naam })), 405 ]; 406 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 } 412 413 if (!collecties.length) { 414 // Ook EEN losse track wordt een playlist: naar buiten toe is er dan altijd 415 // een collectie om naar te wijzen. Hoe Klonkt dat toont is een aparte vraag. 416 return { type: 'playlist', collectie: null, tracks, bonus: [], leentMetadata: true }; 417 } 418 419 if (collecties.length === 1) { 420 const c0 = collecties[0]; 421 // Losse tracks naast een collectie zijn geen rommelrestje maar bonus-tracks, 422 // en dat maakt het geheel een album. 423 if (tracks.length) return { type: 'album', collectie: c0, tracks: [], bonus: tracks, leentMetadata: true }; 424 return { type: c0.soort, collectie: c0, tracks: [], bonus: [], leentMetadata: true }; 425 } 426 427 return { type: 'post', collecties, tracks, bonus: [], leentMetadata: false }; 356 return afleidenUitInsluitingen(content, (id) => playlistKind(id, siteId)); 428 357 } 429 358
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)