You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a GET /api/ipfs/:cid/preview endpoint that fetches audio from IPFS, trims it to a 30-second preview clip, and streams the result back with proper audio headers. Buyers should be able to preview beats before purchasing without downloading the full file.
Why
The frontend SampleDetail page has a play button but no actual audio to play. The IPFS CID points to the full beat (often 3-10MB). Downloading the full file for a preview is wasteful and slow.
Preview clips (~300KB for 30s of MP3) load in under a second on mobile. Full beats take 5-15 seconds on 3G. In the target markets (West Africa, East Africa), this is the difference between a sale and a bounce.
What to build
1. Preview endpoint (GET /api/ipfs/:cid/preview)
GET /api/ipfs/:cid/preview?start=0&duration=30
Range: bytes=0- (optional, for seeking)
Validates CID format (same regex as upload route)
Fetches the full audio from IPFS gateway (Pinata or public gateway)
Pipes through fluent-ffmpeg (or @ffmpeg.wasm/core for zero native deps) to trim to 30 seconds
Streams the result back with Content-Type: audio/mpeg, Accept-Ranges: bytes
Supports Range header for seeking (audio players need this)
Cache-Control: public, max-age=86400 (preview clips are immutable per CID)
Summary
Add a
GET /api/ipfs/:cid/previewendpoint that fetches audio from IPFS, trims it to a 30-second preview clip, and streams the result back with proper audio headers. Buyers should be able to preview beats before purchasing without downloading the full file.Why
src/routes/ipfs.tsfile only has aPOST /uploadroute.What to build
1. Preview endpoint (
GET /api/ipfs/:cid/preview)fluent-ffmpeg(or@ffmpeg.wasm/corefor zero native deps) to trim to 30 secondsContent-Type: audio/mpeg,Accept-Ranges: bytesRangeheader for seeking (audio players need this)public, max-age=86400(preview clips are immutable per CID)2. FFmpeg processing pipeline
fluent-ffmpegwithffmpeg-staticbinary (no system ffmpeg required)3. Caching layer
${cid}:${start}:${duration}/tmp/crate-previews/) for persistence across restarts4. Streaming response
resres.writeHead(200, { 'Content-Type': 'audio/mpeg', 'Transfer-Encoding': 'chunked' })Rangerequests: parse range header, seek ffmpeg to the right byte offset5. Fallback for non-audio CIDs
Dependencies to add
{ "fluent-ffmpeg": "^2.1.3", "ffmpeg-static": "^5.2.0" }Files to create/modify
src/routes/ipfs.ts— addGET /:cid/previewroutesrc/services/previewGenerator.ts— new (ffmpeg pipeline)src/services/previewCache.ts— new (LRU cache)src/middleware/rangeParser.ts— new (HTTP Range header parsing)package.json— add fluent-ffmpeg, ffmpeg-staticAcceptance criteria
GET /api/ipfs/:cid/previewreturns a 30-second MP3 clip?start=param?duration=param (max 60s)Content-Type: audio/mpegandAccept-Ranges: bytesRangeheader is supported for seeking