Index: src/services/PlaylistService.js
===================================================================
--- src/services/PlaylistService.js	(revision 7090fd2a91b6ac5a5468359f0b0064888f4107f7)
+++ src/services/PlaylistService.js	(revision 25dd08f3833da4deb81ef7a027a7e4382dbc5181)
@@ -14,4 +14,5 @@
 import db from '../config/database.js';
 import { v4 as uuid } from 'uuid';
+import { SOORTEN } from '../assets/js/shared/post-music-type.js';
 
 /**
@@ -41,4 +42,29 @@
   return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(s) ? s : null;
 }
+
+/**
+ * De drie soorten die een playlist kan zijn, op EEN plek.
+ *
+ * Stond eerder vijf keer als `data.kind === 'playlist' ? 'playlist' : 'album'`
+ * verspreid over dit bestand. Met twee soorten kon dat nog; bij een derde is
+ * het een fout die staat te wachten, want een van de vijf vergeten betekent dat
+ * een mixtape stilletjes als album wordt opgeslagen en als Album de deur uit
+ * gaat.
+ *
+ * Album blijft de terugval: een onbekende waarde hoort niet stilzwijgend iets
+ * nieuws te worden. De lijst zelf staat in de gedeelde pure module, want de
+ * editor in de browser moet dezelfde drie kennen.
+ */
+export function normKind(v) {
+  const s = String(v == null ? '' : v).trim().toLowerCase();
+  return SOORTEN.includes(s) ? s : 'album';
+}
+
+/**
+ * Draagt deze soort uitgavegegevens? Alleen een album. Een afspeellijst en een
+ * mixtape hebben geen uitgavedatum en geen release-id: het zijn samenstellingen
+ * van andermans of eigen werk, geen uitgave op zichzelf.
+ */
+export const isUitgave = (kind) => normKind(kind) === 'album';
 
 class PlaylistService {
@@ -92,5 +118,5 @@
       year: r.year || 0,
       cover: r.cover_url || '',
-      kind: r.kind || 'album',
+      kind: normKind(r.kind),
       release_date: r.release_date || '',
       mb_release_id: r.mb_release_id || '',
@@ -154,5 +180,5 @@
       year: p.year || 0,
       cover: p.cover_url || fallbackCover,
-      kind: (p.kind === 'playlist') ? 'playlist' : 'album',
+      kind: normKind(p.kind),
       // created_at hoort erbij omdat de AP-kant er `published` van maakt. Zonder
       // dit veld viel buildAlbumObject terug op 1970, en dat stond op 16-8
@@ -162,6 +188,6 @@
       // maar dit is de plek waar de editor leest en die mag niet afhangen van
       // wat er toevallig in de kolom stond.
-      release_date: p.kind === 'playlist' ? '' : (p.release_date || ''),
-      mb_release_id: p.kind === 'playlist' ? '' : (p.mb_release_id || ''),
+      release_date: isUitgave(p.kind) ? (p.release_date || '') : '',
+      mb_release_id: isUitgave(p.kind) ? (p.mb_release_id || '') : '',
       tracks: mappedTracks,
     };
@@ -178,5 +204,5 @@
     const id = this.generateId(siteId, title);
     const now = new Date().toISOString();
-    const kind = data.kind === 'playlist' ? 'playlist' : 'album';
+    const kind = normKind(data.kind);
 
     // Alleen een UITGAVE draagt deze twee. Een afspeellijst heeft geen
@@ -186,5 +212,5 @@
     // voor de post-editor -- en dan staat er stille rommel op een mixtape die
     // later als Album de deur uit gaat.
-    const uitgave = kind === 'album';
+    const uitgave = isUitgave(kind);
     const releaseDate = uitgave ? normDatum(data.release_date) : null;
     const mbRelease = uitgave ? normMbid(data.mb_release_id) : null;
@@ -229,6 +255,6 @@
     // val terug op wat er ligt.
     const nieuwKind = Object.prototype.hasOwnProperty.call(data, 'kind')
-      ? (data.kind === 'playlist' ? 'playlist' : 'album')
-      : (existing.kind || 'album');
+      ? normKind(data.kind)
+      : normKind(existing.kind);
     if (Object.prototype.hasOwnProperty.call(data, 'title')) {
       const v = String(data.title || '').trim();
