Changeset abcf51a in Klonkt
- Timestamp:
- 08/08/2026 04:55:00 PM (4 weeks ago)
- Branches:
- main
- Children:
- 4838760
- Parents:
- 0261730
- git-author:
- Robin <roboburr@…> (08/08/2026 04:54:56 PM)
- git-committer:
- Robin <roboburr@…> (08/08/2026 04:55:00 PM)
- Files:
-
- 1 added
- 2 edited
-
src/services/ActivityPubService.js (modified) (8 diffs)
-
test/activitypub-as2.test.js (modified) (1 diff)
-
test/ap-track-context.test.js (added)
Legend:
- Unmodified
- Added
- Removed
-
src/services/ActivityPubService.js
r0261730 rabcf51a 967 967 * ophaalt niet denkt dat er iets nieuws is. 968 968 */ 969 export function buildTrackCreate(base, site, r ) {970 const audio = buildTrackAudio(base, site, r );969 export function buildTrackCreate(base, site, r, opts = {}) { 970 const audio = buildTrackAudio(base, site, r, opts); 971 971 const me = actorId(base, site.slug); 972 972 return { … … 1006 1006 const items = [ 1007 1007 ...(posts || []).map((p) => buildCreate(base, site, p)), 1008 ...(tracks || []).map((r) => buildTrackCreate(base, site, r)), 1008 // Eén zoekopdracht voor alle tracks samen, niet per stuk. 1009 ...(() => { 1010 const posts = (tracks || []).length && site.id ? trackHostPosts(site.id) : null; 1011 return (tracks || []).map((r) => buildTrackCreate(base, site, r, { hostPosts: posts })); 1012 })(), 1009 1013 ] 1010 1014 .sort((a, b) => wanneer(b) - wanneer(a)) … … 1130 1134 * belooft. 1131 1135 */ 1136 /** 1137 * Bij welke post hoort een track? (shaer-0nh) 1138 * 1139 * Een track staat nooit los in Klonkt: hij wordt getoond BINNEN een post, via 1140 * een van drie insluitingen in posts.content. Die relatie stond alleen in die 1141 * tekst en nergens op de draad -- waardoor Shaer, dat zijn feed uit de outbox 1142 * bouwt, sinds fb22f78 losse Audio-kaarten kreeg zonder inhoud. 1143 * 1144 * ALLES IN EEN ZOEKOPDRACHT, niet per track. De collectie loopt over elke open 1145 * track, en drie LIKE-scans per stuk wordt bij tweehonderd nummers zeshonderd 1146 * scans. Nu is het er een, en de map gaat mee als optie. 1147 * 1148 * De rang bepaalt welke post wint als er meerdere zijn: rechtstreeks ingesloten 1149 * is specifieker dan via een playlist, en die weer specifieker dan via een 1150 * albumnaam. Bij gelijke rang de nieuwste post -- dat is waar iemand hem het 1151 * laatst heeft uitgebracht. 1152 */ 1153 export function trackHostPosts(siteId) { 1154 const rijen = db.prepare(` 1155 SELECT tid, post_id, post_slug, rang, wanneer FROM ( 1156 SELECT t.id AS tid, p.id AS post_id, p.slug AS post_slug, 1 AS rang, 1157 COALESCE(p.published_at, p.created_at) AS wanneer 1158 FROM audio_tracks t 1159 JOIN posts p ON p.site_id = t.site_id AND p.status = 'published' 1160 AND p.content LIKE '%[[track:' || t.id || ']]%' 1161 WHERE t.site_id = ? AND t.fedi_open = 1 1162 UNION ALL 1163 SELECT t.id, p.id, p.slug, 2, COALESCE(p.published_at, p.created_at) 1164 FROM playlist_tracks pt 1165 JOIN audio_tracks t ON t.id = pt.track_id 1166 JOIN posts p ON p.site_id = t.site_id AND p.status = 'published' 1167 AND p.content LIKE '%[[playlist:' || pt.playlist_id || ']]%' 1168 WHERE t.site_id = ? AND t.fedi_open = 1 1169 UNION ALL 1170 SELECT t.id, p.id, p.slug, 3, COALESCE(p.published_at, p.created_at) 1171 FROM audio_tracks t 1172 JOIN posts p ON p.site_id = t.site_id AND p.status = 'published' 1173 AND p.content LIKE '%[[album:' || t.album || ']]%' 1174 WHERE t.site_id = ? AND t.fedi_open = 1 AND t.album IS NOT NULL AND t.album <> '' 1175 ) ORDER BY rang, wanneer DESC 1176 `).all(siteId, siteId, siteId); 1177 const uit = new Map(); 1178 for (const r of rijen) if (!uit.has(r.tid)) uit.set(r.tid, { id: r.post_id, slug: r.post_slug }); 1179 return uit; 1180 } 1181 1132 1182 export function buildTrackAudio(base, site, r, opts = {}) { 1133 1183 const abs = (u) => !u ? null : (/^https?:/i.test(u) ? u : `${base}${u.startsWith('/') ? '' : '/'}${u}`); … … 1136 1186 // representatie die zoveel bytes is en die bitrate heeft, niet het nummer. 1137 1187 // Zo doet Funkwhale het ook. 1188 // De post waar dit nummer in staat. Meegegeven door de collectie (een 1189 // zoekopdracht voor alles), of hier opgezocht als deze track los wordt 1190 // opgehaald. `hostPosts` mag expliciet null zijn: dan is er niets te zoeken. 1191 const post = opts.hostPosts !== undefined 1192 ? (opts.hostPosts && opts.hostPosts.get(r.id)) || null 1193 : ((site.id && trackHostPosts(site.id).get(r.id)) || null); 1194 1138 1195 const bestand = { type: 'Link', href: `${base}/audio/stream/${encodeURIComponent(fn)}`, mediaType: r.mime_type || 'audio/mpeg' }; 1139 1196 if (Number(r.size)) bestand.size = Number(r.size); … … 1152 1209 // track moet zelf kunnen zeggen dat hij openbaar is. 1153 1210 to: [PUBLIC], 1154 url: [bestand], 1211 // De post die dit nummer uitbrengt staat VOORAAN als text/html, precies 1212 // zoals Funkwhale zijn trackpagina zet. Wij hadden dat veld leeg gelaten 1213 // omdat Klonkt geen trackpagina heeft -- maar de post IS waar je het kunt 1214 // horen, en dat is wat zo'n link betekent. 1215 url: [...(post ? [{ type: 'Link', href: `${base}/${post.slug}`, mediaType: 'text/html' }] : []), bestand], 1155 1216 }; 1156 1217 if (r.artist) a.summary = r.artist; // artiest als summary: kaal AS2, geen eigen vocab 1218 // AS2-kern `context`: "de context waarbinnen dit object bestaat". Voor een 1219 // track is dat de post die hem uitbrengt. Daarmee is de relatie die tot nu 1220 // toe alleen in posts.content stond, op de draad te zien -- en kan een lezer 1221 // die de post al heeft dit nummer overslaan in plaats van er een lege kaart 1222 // van te maken. 1223 if (post) a.context = noteId(base, post.id); 1157 1224 if (r.duration) a.duration = `PT${Math.round(r.duration)}S`; 1158 1225 if (r.created_at) a.published = new Date(r.created_at).toISOString(); … … 1201 1268 attributedTo: actorId(base, site.slug), 1202 1269 totalItems: (rows || []).length, 1203 orderedItems: (rows || []).map((r) => buildTrackAudio(base, site, r)), 1270 // Eén zoekopdracht voor alle rijen samen; zie trackHostPosts. 1271 orderedItems: (() => { 1272 const posts = site.id ? trackHostPosts(site.id) : null; 1273 return (rows || []).map((r) => buildTrackAudio(base, site, r, { hostPosts: posts })); 1274 })(), 1204 1275 }; 1205 1276 } … … 1263 1334 // twee keer hetzelfde ding en niet twee dingen die toevallig gelijk klinken. 1264 1335 // De hoes van de playlist dient als terugval voor een track zonder eigen hoes. 1265 const items = (rows || []).map((r) => buildTrackAudio(base, site, r, { coverFallback: playlist.cover_url || null })); 1336 const hostPosts = site.id ? trackHostPosts(site.id) : null; 1337 const items = (rows || []).map((r) => buildTrackAudio(base, site, r, { coverFallback: playlist.cover_url || null, hostPosts })); 1266 1338 const out = { 1267 1339 '@context': AP_CONTEXT, … … 6189 6261 buildActor, buildNote, buildCreate, buildOutbox, buildFollowers, buildFollowing, buildFeatured, 6190 6262 channelUrls, channelCategory, timelineFields, guessMediaType, 6191 siteOpenTracks, openTrack, buildTrackAudio, buildTrackCollection, buildTrackCreate, 6263 siteOpenTracks, openTrack, buildTrackAudio, buildTrackCollection, buildTrackCreate, trackHostPosts, 6192 6264 buildPlaylistCollection, playlistOpenTracks, listPlaylistsAP, playlistLinkTags, 6193 6265 followerCount, deliver, fetchActor, verifyRequest, handleInbox, deliverCreate, deliverDelete, deliverUpdate, deliverActorUpdate, resyncFeaturedPins, -
test/activitypub-as2.test.js
r0261730 rabcf51a 26 26 'content', 'name', 'summary', 'url', 'href', 'mediaType', 27 27 'published', 'updated', 'attributedTo', 'inReplyTo', 'replies', 28 // AS2-kern: de context waarbinnen een object bestaat. Een track wijst ermee 29 // naar de post die hem uitbrengt (shaer-0nh). 30 'context', 28 31 'attachment', 'tag', 'icon', 'image', 'duration', 29 32 'contentMap', 'nameMap', 'summaryMap', // AS2 @language-map counterparts of content/name/summary
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)