Repository navigation
build -> master - #15
Merged
Merged
Conversation
Biome (2.5.4) replaces the ESLint+Prettier pair from the feature notes: one tool, VCS-aware ignores, 2-space/double-quote style matching the existing code. Existing sources reformatted to the enforced style. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
commit-msg hook runs commitlint (config-conventional); pre-commit runs biome check so style violations never reach CI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Runs install (frozen lockfile), build, typecheck, lint, and tests on every PR and on pushes to master/build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
commitlint 21.x needs Node >=22.12; engines.node and .nvmrc now reflect that instead of the stale >=20 floor. Husky shebang review comments declined — v9 shims source these files, see docs/pr-reviews/pr1.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Next.js App Router app deployed via @opennextjs/cloudflare with custom domains webplay.io + www. Vitest renders the page server-side as a CI smoke test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
deploy-web.yml runs opennextjs-cloudflare deploy on pushes to master/build touching apps/web. Feature 1 status doc records done items, deviations (Workers not Vercel), and deferred work. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Least-privilege permissions block on both workflows, per Copilot review on PR #2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- fetchWithRetry: per-attempt timeout, exponential backoff with jitter, Retry-After support; retries 429/5xx/network errors only. - env helper gates credentialed providers; .env.example documents vars. - ElevenLabsMusicAdapter (POST /v1/music, mp3 output to temp file); registered in the generator CLI, which now loads the repo-root .env. - 15 new unit tests (mocked fetch); live smoke test under test:live, excluded from CI. Live status: free-tier key returns HTTP 402 paid_plan_required — the Music API needs a paid plan; live test passes once upgraded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per Copilot review on PR #3: clamp attempts to >=1, combine caller AbortSignal with the timeout signal (and never retry caller aborts), cancel unconsumed retryable response bodies, and only use provider request-ids in file paths when they match a safe charset. Five new tests cover each behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…) and bench harness LocalPythonAdapter runs uv-managed Python generator scripts as argv-only subprocesses; scripts print a JSON result line (LocalScriptResult contract). Priority adapters: acestep (ACE-Step 1.5 checkout via ACESTEP_PROJECT_DIR, turbo variant) and stable-audio-open (gated HF weights, needs HF_TOKEN). musicgen is benchmark-only — Meta's weights are CC-BY-NC, so it is marked licenseTerms:restricted and must not enter commercial rotation. pnpm bench runs the Feature 2 robustness protocol: N sequential generations per provider with latency/failure/cost recorded to bench-results/ as JSON+CSV. 15 new unit tests (mocked subprocess); zero model downloads in CI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…isolation ACE-Step script now receives --project-root explicitly from the adapter instead of guessing from env/cwd; toCsv escapes quotes per RFC 4180 (test added); local adapter test fixtures moved into a per-process mkdtemp dir to avoid cross-worker collisions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PyPI's latest is 0.0.x — the >=1.0.0 pin made uv dependency resolution fail before any install. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
stable-audio-tools 0.0.x pins wandb 0.15.4 -> pathtools, whose build imports the stdlib imp module removed in 3.12; uv otherwise selects 3.14 and the env build fails. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
First real M4 run OOM'd MPS (11.7GiB model vs 18.1GiB cap): enable offload_to_cpu/offload_dit_to_cpu per the repo's own 16GB-tier defaults plus torchao int8_weight_only (overridable via --quantization none). Also ignore runtime .cache/ dirs the model writes. Verified: 30s lofi track generated end-to-end in ~217s. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3/3 runs, 226s mean per 30s clip cold; ~37s pure inference — model load dominates, so the Phase 1 generator worker must keep the model resident. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three real-run fixes: pytorch-lightning is imported by stable_audio_tools at inference time but only ships in its [train] extra; torchaudio.save needs the soundfile backend on macOS; and DiffusionTransformer.apg_project upcasts to float64, which MPS lacks — patched to run that method with double aliased to float32. Verified: 45s stereo clip generated end-to-end in ~513s. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Records what Feature 2's local-model work delivered (adapters, bench, 16GB fixes), M4 results for ACE-Step and Stable Audio Open, owner listening feedback, and the path to webplay.io playback: 4-5 min ACE-Step tracks, resident-model worker, R2 + Liquidsoap crossfade + edge Worker HLS per the Phase 1 notes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
chore: repo tooling and CI quality gates (Phase 0 F1, PR 1/2)
feat: hello world at webplay.io with auto-deploy (Phase 0 F1, PR 2/2)
feat: ElevenLabs Music adapter + retry/env infrastructure (Phase 0 F2, PR 3)
feat: local model adapters + bench harness (Phase 0 F2, PR 4)
- provider-licensing.md: verified license posture per provider (HF metadata + live API evidence), launch-gate confirmation items. - cost-model-generation.md: EL-only ruled out (~$394/mo vs $80 budget); local-primary ~$0; blended premium option ~$26. - listening-test.md: protocol + rating template; owner's informal verdicts recorded. - phase0-feature2-decision.md: GO — ACE-Step primary, SAO textures, EL optional premium, MusicGen excluded. - notes/: dated revision blocks aligning phase0-f3 and phase1-f1..f6 with reality — CF Workers hosting, local-primary generation on the M4, resident-model worker, ffmpeg-prerendered milestone 0 before the Liquidsoap droplet, deferred observability accounts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
wrangler.jsonc in phase1-feature3 steps; generator-on-Mac + simple job table in phase1-feature2 architecture section, per Copilot review. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
docs: phase 0 feature 2 close-out — licensing, cost model, decision (PR 5)
apps/edge-worker serves /stream/{channel}/{file} from the
webplay-stream R2 bucket at stream.webplay.io with never-stale playlist
and immutable segment caching, CORS, and a /health probe (7 tests).
pnpm hls crossfades a directory of tracks with ffmpeg acrossfade,
chops VOD HLS segments, and uploads playlist+segments to R2 via the
Cloudflare API (3 tests). Also makes the acestep factory test hermetic
— it assumed no ACE-Step checkout exists, which is now false on the
dev machine.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Same pattern as deploy-web.yml, scoped to apps/edge-worker paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lib ES2022+WebWorker for explicit worker globals; preflight allows Range and exposes ETag; 405s carry CORS + Allow; parseStreamPath now only serves .m3u8/.ts/.aac so unrelated bucket objects can't leak. buildCrossfadeFilter moved to hlslib.ts — importing it from the test was executing the hls CLI main() at module load. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat: edge streaming worker + milestone-0 crossfade playlist (Phase 1 F3 + F1-M0)
hls.js player (Safari-native fallback) wired to the live stream.webplay.io HLS rotation, with play/pause button, spacebar toggle, autoplay-block handling, and VOD-loop so the milestone-0 rotation feels continuous. Background is images/lofi-beats-1 (owner art), recompressed 2.4MB PNG -> 400KB JPEG, with all three images staged in public/backgrounds for the rotating-backgrounds idea (docs/ideas/rotating-backgrounds.md). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix: lengthen loop fade-out to 4 seconds
feat: unique segment prefix per hls rotation build
Live tracks intermittently had non-diatonic piano chords and off-beat bass. Root causes on the generation side: the caption was too thin to constrain harmony/tempo, and the model ran with no key/BPM conditioning. - acestep_generate.py: expose --keyscale, --bpm, --inference-steps and --guidance-scale (were hardcoded), wired into GenerationParams and echoed in the result JSON for observability. - local.ts: enrich the lofi caption (instruments, production, groove, "in key/in time") via a genre-profile map, and add a per-call musicalArgs hook so model-specific flags never leak to other adapters. - local-models.ts: aceStepMusicalArgs resolves keyscale + BPM from the genre profile (or explicit BPM-range midpoint); steps/guidance stay at turbo defaults unless ACESTEP_* env engages the non-turbo base path. - keyscale + BPM apply on turbo (no cost regression); guidance/steps are the opt-in base-model quality tier (ACESTEP_VARIANT=acestep-v15-base). - Document the new env knobs; add unit tests for the caption enrichment, the musicalArgs passthrough, and env overrides. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
buildLocalPrompt used the genre-profile BPM even when the request carried an explicit BPM range, while aceStepMusicalArgs derived the range midpoint for --bpm — so the caption could say "75 BPM" while the model got --bpm 85. Give an explicit request BPM precedence over the profile default in the caption too, matching the structured-arg precedence. Add a test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The genre-profile caption from #10 ("clean / steady / in time / no dissonance") fixed the off chords and off-beat bass but sounded sterile. An owner A/B (2026-07-17) landed on warmer, tape-saturated wording, which is now live on webplay.io. Bake it into GENRE_PROFILES.lofi so every future `pnpm generate` lofi track inherits the vibe instead of the sterile default. keyscale=C Major + bpm=75 still lock harmony/tempo, so the caption is free to describe character. Update the caption test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…controls feat: ACE-Step harmony/rhythm controls to tame off chords & bass
feat: fold approved "true Lofi Beats" wording into the lofi profile
.env.example was a comment-only template (no secret values); drop it from the tree and ignore .env.* so no env files are tracked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
.env.example was removed from the tree, taking the Cloudflare/Kinde/ provider/local-generation env documentation with it. Move that reference into docs/environment.md and repoint the requireEnv() error message at it so the config surface (incl. the ACESTEP_* generation knobs) stays discoverable without tracking any .env file. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the Calm Music for Sleep channel profile (F Major, 58 BPM, beatless airy/heavenly caption palette) and exposes the already-supported seed parameter through pnpm generate for reproducible renders. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Channel config lives in apps/web/lib/channels.ts; ChannelShell (client) owns the selected channel and renders background, tagline and a glassmorphic pill dropdown. AudioPlayer now takes streamUrl as a prop and is remounted per channel. Adding a channel is one config entry plus a background image. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First-draft palette (choir aahs, string swells, cathedral reverb) missed the vibe; replaced with the owner-supplied deep-sleep-ambient wording. Adds the compressed calm-sleep-1 background and points the sleep channel at it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Analysis of the reference (librosa): spectral centroid ~300 Hz, pulse clarity 0.4, 0.1% percussive energy, ~2 dB RMS variance — a deep dark steady drone. Drafts v1/v2 measured 5-25x off on all four. Caption now uses dark/still/drone wording, and GenreProfile.bpm becomes optional: beatless genres must not send --bpm, which conditions a beat grid in. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop "no drums, no percussion, no beat" (with CFG the caption still contains "drums"); "beatless" carries the constraint as a positive tag. Switcher labels use owner wording: "Lofi Beats for Study" / "Calm Music for Sleep". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: Calm Music for Sleep channel + channel switcher
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…istry - jazz genre profile in adapters: Bb Major, 90 BPM relaxed swing, lounge trio + tenor sax caption (positive tags only) - stream live at stream.webplay.io/stream/jazz/playlist.m3u8 (3x300s crossfaded, build prefix r1784415298873, 224 segments) - channel switcher entry + jazz-1.jpg background (owner-supplied) - docs/channel-seeds.md: per-channel seed registry for reproducing live tracks and rendering future ones Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat: jazz channel "Jazz for Lounging"
- CreatorMenu client component: glassmorphic hamburger button fixed top-left, sliding left drawer with "Created by Jerry Chen" header and external links to Github and Buy me a Coffee (new tab, noopener) - click-away backdrop closes the drawer; matches existing pill styling - README rewritten for curious listeners arriving from webplay.io: describes the generate/curate/stream pipeline and repo layout without naming specific tools or services Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat: creator hamburger menu + listener-facing README
There was a problem hiding this comment.
Pull request overview
This PR syncs the build branch into master by introducing the repo’s monorepo tooling + CI/CD, plus the core building blocks for webplay: adapter-based music generation, a generator CLI/bench/HLS builder, an edge Worker to serve HLS from R2, and a minimal Next.js player.
Changes:
- Establish pnpm workspace + shared TypeScript/Biome/commitlint/husky configuration and GitHub Actions (CI + deploy).
- Add
@webplay/adapters(local Python model runner + ElevenLabs adapter + retry/env utilities) with unit + live-test separation. - Add generator tooling (CLI, bench harness, offline HLS build+upload) plus the Cloudflare edge worker and web UI.
Reviewed changes
Copilot reviewed 89 out of 96 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.base.json | Shared strict TS base config |
| README.md | Expanded project overview and structure |
| pnpm-workspace.yaml | Defines pnpm workspace packages |
| packages/adapters/vitest.live.config.ts | Live-test vitest config + .env loading |
| packages/adapters/vitest.config.ts | Default vitest config excluding live tests |
| packages/adapters/tsconfig.json | Adapters TS build config |
| packages/adapters/test/mock.test.ts | Mock adapter unit tests |
| packages/adapters/test/local.test.ts | Local adapter + prompt/tests |
| packages/adapters/test/local-models.test.ts | Local model factory + musical args tests |
| packages/adapters/test/live/elevenlabs.live.test.ts | ElevenLabs live smoke test |
| packages/adapters/test/http.test.ts | fetch retry logic unit tests |
| packages/adapters/test/elevenlabs.test.ts | ElevenLabs adapter unit tests |
| packages/adapters/src/types.ts | Core adapter types + AdapterError |
| packages/adapters/src/mock.ts | Mock WAV-synth adapter |
| packages/adapters/src/local.ts | Local uv/python adapter + genre profiles |
| packages/adapters/src/local-models.ts | Local model factory helpers |
| packages/adapters/src/index.ts | Package public exports |
| packages/adapters/src/http.ts | fetchWithRetry + backoff utilities |
| packages/adapters/src/env.ts | Env helpers + credential checks |
| packages/adapters/src/elevenlabs.ts | ElevenLabs Music adapter |
| packages/adapters/package.json | Adapters package scripts/metadata |
| package.json | Repo-level scripts + tooling deps |
| notes/phase1-feature6.md | Phase 1 feature 6 revisions |
| notes/phase1-feature4.md | Phase 1 feature 4 revisions |
| notes/phase1-feature3.md | Phase 1 feature 3 revisions |
| notes/phase1-feature2.md | Phase 1 feature 2 revisions |
| notes/phase1-feature1.md | Phase 1 feature 1 revisions |
| notes/phase0-feature3.md | Phase 0 feature 3 revisions |
| notes/phase0-feature2.md | Phase 0 feature 2 status update |
| notes/phase0-feature1-status.md | Phase 0 feature 1 status doc |
| docs/provider-licensing.md | Provider licensing summary + open items |
| docs/pr-reviews/pr9.md | Stored review notes for PR #9 |
| docs/pr-reviews/pr7.md | Stored review notes for PR #7 |
| docs/pr-reviews/pr6.md | Stored review notes for PR #6 |
| docs/pr-reviews/pr5.md | Stored review notes for PR #5 |
| docs/pr-reviews/pr4.md | Stored review notes for PR #4 |
| docs/pr-reviews/pr3.md | Stored review notes for PR #3 |
| docs/pr-reviews/pr2.md | Stored review notes for PR #2 |
| docs/pr-reviews/pr10.md | Stored review notes for PR #10 |
| docs/pr-reviews/pr1.md | Stored review notes for PR #1 |
| docs/phase0-feature2-local-generation.md | Phase 0 Feature 2 recap/results |
| docs/phase0-feature2-decision.md | Provider decision doc + conditions |
| docs/listening-test.md | Listening test protocol scaffold |
| docs/ideas/rotating-backgrounds.md | Idea doc for rotating backgrounds |
| docs/environment.md | Environment variable documentation |
| docs/cost-model-generation.md | Generation cost model doc |
| docs/channel-seeds.md | Channel seed registry doc |
| docs/bench-m4-local.md | Local generation benchmark results |
| commitlint.config.mjs | Commitlint conventional config |
| biome.json | Biome lint/format configuration |
| apps/web/wrangler.jsonc | Web worker wrangler config |
| apps/web/vitest.config.ts | Web vitest config (JSX transform) |
| apps/web/tsconfig.json | Web app TS config |
| apps/web/test/page.test.tsx | SSR smoke tests for landing page |
| apps/web/test/audio-player.test.tsx | AudioPlayer unit tests |
| apps/web/package.json | Web app dependencies/scripts |
| apps/web/open-next.config.ts | OpenNext Cloudflare config |
| apps/web/next.config.ts | Next.js config stub |
| apps/web/lib/channels.ts | Channel definitions + defaults |
| apps/web/components/CreatorMenu.tsx | Slide-out creator links menu |
| apps/web/components/ChannelShell.tsx | Channel picker + page shell |
| apps/web/components/AudioPlayer.tsx | HLS audio player w/ loop + fade |
| apps/web/app/page.tsx | Main page composition |
| apps/web/app/layout.tsx | Next layout + metadata |
| apps/generator/tsconfig.json | Generator TS config |
| apps/generator/test/hls.test.ts | HLS crossfade filter tests |
| apps/generator/test/benchlib.test.ts | Bench harness tests |
| apps/generator/src/registry.ts | Adapter registry + lookup |
| apps/generator/src/hlslib.ts | ffmpeg acrossfade filter builder |
| apps/generator/src/hls.ts | Offline HLS build + optional R2 upload |
| apps/generator/src/cli.ts | Generator CLI (single track) |
| apps/generator/src/benchlib.ts | Bench runner + CSV formatting |
| apps/generator/src/bench.ts | Bench CLI command |
| apps/generator/py/stable_audio_generate.py | Stable Audio Open generation script |
| apps/generator/py/musicgen_generate.py | MusicGen benchmark-only script |
| apps/generator/py/acestep_generate.py | ACE-Step generation script |
| apps/generator/package.json | Generator package scripts/deps |
| apps/edge-worker/wrangler.jsonc | Edge worker wrangler config |
| apps/edge-worker/tsconfig.json | Edge worker TS config (WebWorker libs) |
| apps/edge-worker/test/index.test.ts | Edge worker unit tests |
| apps/edge-worker/src/index.ts | R2-backed HLS serving worker |
| apps/edge-worker/package.json | Edge worker package metadata |
| .nvmrc | Pin Node version (22.22.2) |
| .husky/pre-commit | Pre-commit hook (pnpm lint) |
| .husky/commit-msg | Commit-msg hook (commitlint) |
| .gitignore | Expanded ignores for outputs/artifacts |
| .github/workflows/deploy-web.yml | Deploy workflow for web app |
| .github/workflows/deploy-edge.yml | Deploy workflow for edge worker |
| .github/workflows/ci.yml | CI workflow (build/typecheck/lint/test) |
| .editorconfig | Editor defaults (2-space indent, etc.) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+27
to
+32
| const html = renderToStaticMarkup(<Home />); | ||
| expect(html).toContain("Created by Jerry Chen"); | ||
| expect(html).toContain("Github"); | ||
| expect(html).toContain("https://github.com/jerryschen31"); | ||
| expect(html).toContain("Buy me a Coffee"); | ||
| expect(html).toContain("https://buymeacoffee.com/jerryschen7"); |
Owner
Author
There was a problem hiding this comment.
Fixed in 32bbc96 — test now expects "GitHub", matching the corrected label.
- CreatorMenu: "Github" -> "GitHub" (correct branding), and the matching SSR test expectation in page.test.tsx. - hls.ts: encode R2 object key path segments individually so the "/" separator is preserved instead of being percent-encoded into the key. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.