| 1 | /**
|
|---|
| 2 | * AudioStreamService — builds URLs for the audio streaming route.
|
|---|
| 3 | *
|
|---|
| 4 | * ┌─ ANTI-THEFT MODEL (Spotify-flavoured, step 1 — 2026-05-20) ─────────────┐
|
|---|
| 5 | * │ audioUrl() returns a plain /audio/stream/<filename> path. There is NO │
|
|---|
| 6 | * │ signed/expiring token in the URL — that earlier design baked a single │
|
|---|
| 7 | * │ 10-min deadline into a whole queue at render time, so later tracks' │
|
|---|
| 8 | * │ tokens expired mid-session and the player looped "next" forever. │
|
|---|
| 9 | * │ │
|
|---|
| 10 | * │ Protection now lives in two NON-expiring layers, so it can't cause that │
|
|---|
| 11 | * │ failure again: │
|
|---|
| 12 | * │ 1. Client (audio-player.js) fetch()es the bytes and plays from a │
|
|---|
| 13 | * │ blob: object URL — no shareable link, no "save audio as". │
|
|---|
| 14 | * │ 2. Server (routes/audio.js) gates /audio/stream to same-origin │
|
|---|
| 15 | * │ browser fetches — blocks address-bar paste, hotlinks, curl/yt-dlp. │
|
|---|
| 16 | * │ │
|
|---|
| 17 | * │ FUTURE STEPS (deliberate, tested one at a time): │
|
|---|
| 18 | * │ - step 2: MSE chunked/progressive streaming (true Spotify feel) │
|
|---|
| 19 | * │ - step 3: per-session short-lived token in a header, minted JIT │
|
|---|
| 20 | * │ - step 4: light byte obfuscation (XOR/key) on the wire │
|
|---|
| 21 | * └──────────────────────────────────────────────────────────────────────┘
|
|---|
| 22 | */
|
|---|
| 23 |
|
|---|
| 24 | /**
|
|---|
| 25 | * Build the public stream URL for an audio filename.
|
|---|
| 26 | * Returns null for a falsy filename so callers can guard playability.
|
|---|
| 27 | */
|
|---|
| 28 | export function audioUrl(filename) {
|
|---|
| 29 | if (!filename) return null;
|
|---|
| 30 | return `/audio/stream/${encodeURIComponent(filename)}`;
|
|---|
| 31 | }
|
|---|
| 32 |
|
|---|
| 33 | export default { audioUrl };
|
|---|