5.3 KiB
Executable file
5.3 KiB
Executable file
Task 2-a — v6.7 part A (server): resolve anti-spam semaphore + music-service framework
Task ID: 2-a Agent: full-stack-developer Status: COMPLETE — lint clean, tsc clean on all four files, verified live against the dev server (curl) and at the process level (ps snapshots).
Scope (ONLY these files — nothing else touched)
src/lib/music-services.ts(modified)src/lib/server/music-providers.ts(NEW)src/app/api/resolve/route.ts(modified)src/components/ava/settings-tab.tsx(modified — Music services chips only)
What was built
1. src/lib/music-services.ts
export type StreamStatus = 'live' | 'coming-soon'ServiceDefgained requiredstreamStatus: StreamStatus+ optionalcomingSoonNote?: string(required when coming-soon).- youtube / soundcloud →
'live'; spotify / apple →'coming-soon'with notes like: "Spotify playback is coming in a later update — your saved Spotify login will be used automatically the moment it ships. Meanwhile, YouTube, SoundCloud and direct-file links work today." - All pre-existing fields/exports intact (
SERVICES,serviceDef,serviceForUrl,isServiceId,ServiceId).
2. src/lib/server/music-providers.ts — exact exports
export type ProviderStatus = 'live' | 'coming-soon'
export interface ProviderAdapter {
id: ServiceId | 'generic'
label: string
status: ProviderStatus
comingSoonNote: string
handlesUrl(url: string): boolean
// commented v6.8+ extension points:
// resolveTrack?(url, auth) / listPlaylist?(url, auth)
}
export const YTDLP_PROVIDER: ProviderAdapter // id 'generic', live, handlesUrl → true
export const SPOTIFY_PROVIDER: ProviderAdapter // id 'spotify', coming-soon
export const APPLE_PROVIDER: ProviderAdapter // id 'apple', coming-soon
export function providerForUrl(url: string): ProviderAdapter
- Domain matching mirrors
serviceForUrl(host/subdomain, "www." ignored) scoped to the one service. providerForUrlis status-agnostic: flip an adapter to 'live' and it still owns its URLs at the route's dispatch point.- Big "HOW TO SHIP A NEW SERVICE (v6.8+)" header comment: 3-step recipe (implement resolveTrack/listPlaylist with the exact payload shapes → flip streamStatus in music-services.ts + status here → extend the marked dispatch spot in the route).
3. src/app/api/resolve/route.ts
- Coming-soon dispatch right after
assertPublicHttpUrl, BEFORE cache/direct-audio/yt-dlp:{ ok: false, code: 'coming-soon', service: provider.id, error: provider.comingSoonNote }(422). Spotify/Apple links never spawn yt-dlp. Generic "That site is not supported" 422 kept but without the DRM parenthetical. - Semaphore:
YTDLP_MAX_CONCURRENCY = 2,YTDLP_QUEUE_TIMEOUT_MS = 40_000, classYtdlpSemaphoreon globalThis (__avaYtdlpSemaphore). FIFO waiters, direct slot handoff inrelease()(active count unchanged). Acquire wrapped aroundrunYtDlpwith aheldflag → release exactly once in the finally; busy → 503 "The resolver is busy right now — try again in a moment."; timed-out waiter that was granted a slot in the same tick does NOT double-release (queue membership is the single source of truth). Cache hits + direct-file links bypass the gate. - Fixed a pre-existing strict-type bug in
buildCookieParts(pairs: string[]receiving[string,string]tuples → now[string, string][]; no runtime change).
4. src/components/ava/settings-tab.tsx
- Coming-soon service chips get an amber "soon" badge:
<span className="rounded bg-amber-400/15 px-1 py-px text-[8px] font-semibold uppercase tracking-wider text-amber-300">soon</span>(placed between name and the check/— marker). Nothing else changed; login buttons fully functional for coming-soon services.
Verification
bun run lintclean;bunx tsc --noEmitclean for all four files.- Live: spotify track/playlist + apple album → 422 coming-soon in ~6ms (zero yt-dlp); SSRF gate still first (localhost → 400); direct MP3 → 200; spotify+cookies → coming-soon before cookie staging; real YouTube resolve still passes through (bot-wall 422 as before).
- End-to-end anti-spam: 6 concurrent resolves of the same YouTube link —
ps snapshots show ≤2 top-level
bin/yt-dlpprocesses at any moment (extra PIDs = the onefile binary's own self-exec children, not extra extractions); FIFO waves of 2; zero 503s; dev.log error-free. - Scratch semaphore tests (cap/FIFO/timeout/handoff/no-leak) all pass; scratch dir deleted.
Notes for the lead agent
- apple.com is broad: the apple ServiceDef domains include
apple.com, so ALL apple.com links (e.g. podcasts.apple.com, which yt-dlp's ApplePodcasts extractor could handle) now get the Apple Music coming-soon answer. Consistent with client-side matching; say the word to scope the adapter to music.apple.com only. - Worklog has no v6.6 section (the v6.6 music-login files were committed inside the v6.5 commit 8fa0eb9); my entry follows v6.5 directly.
- Not-mine tsc errors (untouched): examples/websocket/* (missing deps), api/rust-scaffold (Uint8Array vs BodyInit), ava/audio-source.ts ('never' narrowing — the concurrent client agent's WIP).
- Did NOT commit; did NOT touch version.ts / page.tsx / audio.ts / queue files / link-dialog / audio-source / queue-bar.