| 1 | /**
|
|---|
| 2 | * De regel: welk soort uitgave is een post? (shaer-cyg)
|
|---|
| 3 | *
|
|---|
| 4 | * WAAROM DIT ONDER assets/ STAAT. De regel wordt op twee plaatsen gebruikt: de
|
|---|
| 5 | * server leidt er het type van bestaande posts mee af, en de editor laat het
|
|---|
| 6 | * type er live door volgen als je muziek invoegt. Dat MOET dezelfde regel zijn.
|
|---|
| 7 | * Twee kopieën zouden niet luidruchtig kapotgaan maar stil uit elkaar lopen --
|
|---|
| 8 | * de editor zegt album, de server zegt playlist, en niemand die het merkt. De
|
|---|
| 9 | * browser kan alleen bij /assets, de server kan overal bij; dus staat hij hier,
|
|---|
| 10 | * en importeert de server hem vanuit services/music.
|
|---|
| 11 | *
|
|---|
| 12 | * Deze module is bewust PUUR: geen database, geen DOM, geen fetch. Wie hem
|
|---|
| 13 | * gebruikt levert zelf een opzoeker voor de soort van een playlist.
|
|---|
| 14 | *
|
|---|
| 15 | * DE REGEL GAAT OVER IDENTITEIT, NIET OVER TELLEN (Robins herformulering, 9-8):
|
|---|
| 16 | * een post neemt het type van zijn muziek over als hij precies EEN muzikale
|
|---|
| 17 | * eenheid bevat. Zijn het er meer, dan is de post een post die naar muziek
|
|---|
| 18 | * verwijst, en houden de collecties hun eigen identiteit.
|
|---|
| 19 | *
|
|---|
| 20 | * losse track(s) -> playlist metadata van de post geleend
|
|---|
| 21 | * een collectie -> die soort metadata van de post geleend
|
|---|
| 22 | * een collectie + losse tracks -> album de losse zijn BONUS-TRACKS
|
|---|
| 23 | * een MIXTAPE + losse tracks -> mixtape de losse zijn BONUS-TRACKS
|
|---|
| 24 | * twee of meer collecties -> post NIETS geleend
|
|---|
| 25 | *
|
|---|
| 26 | * Die vierde regel is de uitzondering op de derde en staat er met opzet naast:
|
|---|
| 27 | * bonustracks maken van een uitgave een album, maar een bandje is geen uitgave.
|
|---|
| 28 | *
|
|---|
| 29 | * Waarom de lening bij de laatste vervalt: die bestaat omdat een collectie soms
|
|---|
| 30 | * dun is -- geen eigen hoes, geen eigen titel. Bij twee is de post niet meer de
|
|---|
| 31 | * drager van EEN identiteit, en vervalt de reden vanzelf.
|
|---|
| 32 | *
|
|---|
| 33 | * WIE KIEST ALBUM OF PLAYLIST: dat gebeurt wanneer de PLAYLIST wordt gemaakt.
|
|---|
| 34 | * Die keuze staat in playlists.kind en wordt hier OPGEZOCHT, niet afgeleid --
|
|---|
| 35 | * een post om een album is een album, ook al heet de shortcode [[playlist:...]].
|
|---|
| 36 | *
|
|---|
| 37 | * EN ALS WE HET NIET WETEN: een gewone post met insluitingen die de post als
|
|---|
| 38 | * context hebben. Geen noodgreep maar de rustende toestand; tracks wijzen met
|
|---|
| 39 | * `context` toch al terug naar hun post, dus er gaat niets verloren.
|
|---|
| 40 | */
|
|---|
| 41 |
|
|---|
| 42 | // Dezelfde patronen als de renderer in AudioEmbedService: wat daar niet
|
|---|
| 43 | // insluit, telt hier niet mee. Anders zou een shortcode die niets oplevert wel
|
|---|
| 44 | // het type van de post kunnen bepalen.
|
|---|
| 45 | // De soorten die een playlist kan zijn, en dit is de ENIGE lijst. PlaylistService
|
|---|
| 46 | // en music/index.js halen hem hier op, precies omdat het andersom niet kan:
|
|---|
| 47 | // deze module draait ook in de browser en mag niets van de database weten.
|
|---|
| 48 | //
|
|---|
| 49 | // Waarom dat uitmaakt: bij twee soorten stond de keuze vijf keer als
|
|---|
| 50 | // `x === 'playlist' ? 'playlist' : 'album'` verspreid over drie bestanden. Zo'n
|
|---|
| 51 | // vorm valt niet om bij een derde soort, hij slikt hem -- een mixtape werd
|
|---|
| 52 | // stilzwijgend een album en ging als Album de deur uit.
|
|---|
| 53 | export const SOORTEN = ['album', 'playlist', 'mixtape'];
|
|---|
| 54 |
|
|---|
| 55 | const RE_PLAYLIST = /\[\[playlist:([a-z0-9][a-z0-9-]*)\]\]/gi;
|
|---|
| 56 | const RE_ALBUM = /\[\[album:([^\]]+)\]\]/g;
|
|---|
| 57 | const RE_TRACK = /\[\[track:([A-Za-z0-9_-]+)\]\]/g;
|
|---|
| 58 |
|
|---|
| 59 | /**
|
|---|
| 60 | * @param {string} content de HTML/tekst van de post
|
|---|
| 61 | * @param {(id: string) => ('album'|'playlist'|null)} kindVan
|
|---|
| 62 | * de gekozen soort van een playlist, of null als hij onbekend is
|
|---|
| 63 | * @returns {null|{type, collectie?, collecties?, tracks, bonus, onbekend?, leentMetadata}}
|
|---|
| 64 | */
|
|---|
| 65 | export function afleidenUitInsluitingen(content, kindVan) {
|
|---|
| 66 | const c = String(content || '');
|
|---|
| 67 | const zoek = typeof kindVan === 'function' ? kindVan : () => null;
|
|---|
| 68 | const uniek = (re) => [...new Set([...c.matchAll(re)].map((m) => m[1].trim()))];
|
|---|
| 69 |
|
|---|
| 70 | const playlists = uniek(RE_PLAYLIST);
|
|---|
| 71 | const albums = uniek(RE_ALBUM);
|
|---|
| 72 | const tracks = uniek(RE_TRACK);
|
|---|
| 73 |
|
|---|
| 74 | if (!playlists.length && !albums.length && !tracks.length) return null;
|
|---|
| 75 |
|
|---|
| 76 | // De soort van een playlist is een gegeven, geen afleiding.
|
|---|
| 77 | const onbekend = [];
|
|---|
| 78 | const uitPlaylists = playlists.map((id) => {
|
|---|
| 79 | const kind = zoek(id);
|
|---|
| 80 | if (!SOORTEN.includes(kind)) { onbekend.push(id); return null; }
|
|---|
| 81 | return { soort: kind, id };
|
|---|
| 82 | }).filter(Boolean);
|
|---|
| 83 |
|
|---|
| 84 | const collecties = [
|
|---|
| 85 | ...uitPlaylists,
|
|---|
| 86 | ...albums.map((naam) => ({ soort: 'album', naam })),
|
|---|
| 87 | ];
|
|---|
| 88 |
|
|---|
| 89 | // Onbekende situatie -> gewone post. De insluitingen blijven staan en houden
|
|---|
| 90 | // de post als context; alleen het label wordt niet verzonnen.
|
|---|
| 91 | if (onbekend.length) {
|
|---|
| 92 | return { type: 'post', collecties, tracks, bonus: [], onbekend, leentMetadata: false };
|
|---|
| 93 | }
|
|---|
| 94 |
|
|---|
| 95 | if (!collecties.length) {
|
|---|
| 96 | // Ook EEN losse track wordt een playlist: naar buiten toe is er dan altijd
|
|---|
| 97 | // een collectie om naar te wijzen. Hoe Klonkt dat toont is een aparte vraag.
|
|---|
| 98 | return { type: 'playlist', collectie: null, tracks, bonus: [], leentMetadata: true };
|
|---|
| 99 | }
|
|---|
| 100 |
|
|---|
| 101 | if (collecties.length === 1) {
|
|---|
| 102 | const c0 = collecties[0];
|
|---|
| 103 | // EEN MIXTAPE BLIJFT EEN MIXTAPE, ook met losse tracks erbij. De regel
|
|---|
| 104 | // hieronder maakt van collectie + losse tracks een album met bonustracks, en
|
|---|
| 105 | // dat klopt voor een uitgave: extra nummers bij een plaat zijn bonus. Een
|
|---|
| 106 | // bandje is geen uitgave. Er een album van maken omdat er een los nummer
|
|---|
| 107 | // naast staat zou het ding hernoemen op grond van iets wat er niet bij
|
|---|
| 108 | // hoort. Album en playlist houden bewust hun oude gedrag: dat zijn
|
|---|
| 109 | // bestaande posts en die mogen hier niet stilletjes van soort wisselen.
|
|---|
| 110 | if (c0.soort === 'mixtape') {
|
|---|
| 111 | return { type: 'mixtape', collectie: c0, tracks: [], bonus: tracks, leentMetadata: true };
|
|---|
| 112 | }
|
|---|
| 113 | // Losse tracks naast een collectie zijn geen rommelrestje maar bonus-tracks,
|
|---|
| 114 | // en dat maakt het geheel een album.
|
|---|
| 115 | if (tracks.length) return { type: 'album', collectie: c0, tracks: [], bonus: tracks, leentMetadata: true };
|
|---|
| 116 | return { type: c0.soort, collectie: c0, tracks: [], bonus: [], leentMetadata: true };
|
|---|
| 117 | }
|
|---|
| 118 |
|
|---|
| 119 | return { type: 'post', collecties, tracks, bonus: [], leentMetadata: false };
|
|---|
| 120 | }
|
|---|
| 121 |
|
|---|
| 122 | /** De playlist-ids die in een tekst worden ingesloten. */
|
|---|
| 123 | export function ingeslotenPlaylists(content) {
|
|---|
| 124 | return [...new Set([...String(content || '').matchAll(RE_PLAYLIST)].map((m) => m[1].trim()))];
|
|---|
| 125 | }
|
|---|