Index: src/services/music/index.js
===================================================================
--- src/services/music/index.js	(revision 5ae4d61c76ecec8dcb8eaeeb0ff5379e69be6f02)
+++ src/services/music/index.js	(revision aed0092c4492bca99e1c9a5c82332c1d08fe1bee)
@@ -14,5 +14,5 @@
 
 import db from '../../config/database.js';
-import { AP_CONTEXT, PUBLIC, actorId, noteId, safeUrl, guessMediaType, buildHashtagList } from '../ap-core.js';
+import { AP_CONTEXT, PUBLIC, actorId, noteId, safeUrl, guessMediaType, buildHashtagList, pagedCollection } from '../ap-core.js';
 import { afleidenUitInsluitingen, ingeslotenPlaylists } from '../../assets/js/shared/post-music-type.js';
 
@@ -201,16 +201,8 @@
 /** De collectie van alle open tracks van een site (shaer-0nh, stap 3). */
 export function buildTrackCollection(base, site, rows) {
-  return {
-    '@context': AP_CONTEXT,
-    id: `${actorId(base, site.slug)}/tracks`,
-    type: 'OrderedCollection',
-    attributedTo: actorId(base, site.slug),
-    totalItems: (rows || []).length,
-    // Eén zoekopdracht voor alle rijen samen; zie trackHostPosts.
-    orderedItems: (() => {
-      const posts = site.id ? trackHostPosts(site.id) : null;
-      return (rows || []).map((r) => buildTrackAudio(base, site, r, { hostPosts: posts }));
-    })(),
-  };
+  // Eén zoekopdracht voor alle rijen samen; zie trackHostPosts.
+  const posts = site.id ? trackHostPosts(site.id) : null;
+  const items = (rows || []).map((r) => buildTrackAudio(base, site, r, { hostPosts: posts }));
+  return pagedCollection(`${actorId(base, site.slug)}/tracks`, items, { extra: { attributedTo: actorId(base, site.slug) } });
 }
 
@@ -270,12 +262,5 @@
     return stub;
   });
-  return {
-    '@context': AP_CONTEXT,
-    id: colId,
-    type: 'OrderedCollection',
-    attributedTo: actorId(base, site.slug),
-    totalItems: items.length,
-    orderedItems: items,
-  };
+  return pagedCollection(colId, items, { extra: { attributedTo: actorId(base, site.slug) } });
 }
 
@@ -289,13 +274,7 @@
   const hostPosts = site.id ? trackHostPosts(site.id) : null;
   const items = (rows || []).map((r) => buildTrackAudio(base, site, r, { coverFallback: playlist.cover_url || null, hostPosts }));
-  const out = {
-    '@context': AP_CONTEXT,
-    id: `${actorId(base, site.slug)}/playlists/${playlist.id}`,
-    type: 'OrderedCollection',
-    name: playlist.title,
-    attributedTo: actorId(base, site.slug),
-    totalItems: items.length,
-    orderedItems: items,
-  };
+  const out = pagedCollection(`${actorId(base, site.slug)}/playlists/${playlist.id}`, items, {
+    extra: { name: playlist.title, attributedTo: actorId(base, site.slug) },
+  });
   // Album of playlist is presentatie; op de draad is het één samenvattingsveld.
   const parts = [];
@@ -449,12 +428,7 @@
 
   const hostPosts = new Map(rows.map((r) => [r.id, { id: post.id, slug: post.slug }]));
-  const out = {
-    '@context': AP_CONTEXT,
-    id: postTracksId(base, site, post.id),
-    type: 'OrderedCollection',
-    attributedTo: actorId(base, site.slug),
-    totalItems: rows.length,
-    orderedItems: rows.map((r) => buildTrackAudio(base, site, r, { hostPosts })),
-  };
+  const out = pagedCollection(postTracksId(base, site, post.id),
+    rows.map((r) => buildTrackAudio(base, site, r, { hostPosts })),
+    { extra: { attributedTo: actorId(base, site.slug) } });
   return leenVanPost(base, site, out, post);
 }
