MusicScope is a local, single-user music intelligence graduation project. It will combine listening imports, explainable recommendations, taste timelines, personal memories, lightweight concert discovery, and cached two-stem audio separation.
The personal user is 00000000-0000-0000-0000-000000000001. Deterministic
demo data belongs to the separate demo user
00000000-0000-0000-0000-000000000999 and is never mixed into normal personal
API results.
The foundation uses a Next.js/TypeScript web client, a FastAPI/Python API, PostgreSQL, and Alembic migrations. External music providers are optional adapters; CSV/manual import and deterministic demo data remain independent of them.
Requirements:
- Node.js 20+
- Python 3.13 for the API (a project-local environment is recommended)
- Python 3.12 for the isolated audio separator runtime
- uv
- FFmpeg 9+ available on
PATHfor audio validation and normalization - Docker Desktop with the Docker daemon running
-
Copy
.env.exampleto.env. -
Start PostgreSQL:
docker compose up -d postgres
-
Install API dependencies and run migrations:
uv venv .venv uv pip install --python .venv/bin/python -r apps/api/requirements.txt cd apps/api ../../.venv/bin/alembic upgrade head ../../.venv/bin/uvicorn app.main:app --reload --port 8000 -
In another terminal, install and start the web app:
cd apps/web npm install npm run dev
Open http://localhost:3000. The API health endpoint is http://localhost:8000/health, and interactive API documentation is available at http://localhost:8000/docs.
The API keeps audio dependencies out of the main Python environment. Install FFmpeg and create the approved isolated separator environment once:
brew install ffmpeg python@3.12
/opt/homebrew/bin/python3.12 -m venv .audio-venv312
.audio-venv312/bin/python -m pip install demucs-infer==4.2.0 soundfile==0.14.0The first real separation downloads the htdemucs checkpoint. The API uses
MPS when PyTorch reports it available and otherwise falls back to CPU. Audio
uploads are normalized to stereo 44.1 kHz WAV, then separated into cached
Vocals and Instrumental artifacts. The local demo accepts WAV, MP3, M4A/AAC,
and FLAC files up to 250 MB and 15 minutes.
Run the real one-file smoke test from the repository root after PostgreSQL, FFmpeg, and the isolated runtime are ready:
PYTHONPATH=apps/api .venv/bin/python scripts/audio_smoke.pyThe baseline CSV importer accepts artist and title columns. Rows with
behavioral fields such as played_at are listening-history imports and may
create ListeningEvent rows only when a canonical track resolves. Optional
columns include album, duration_played_ms, completion_ratio,
event_type, favorite, source_record_id, and source.
Playlist-text imports are library imports: they create or reuse canonical
artists/tracks, add UserLibraryTrack membership, preserve raw provenance,
and create zero listening events. Library membership is long-term preference
evidence, not proof that a track was played. Short-term state and Timeline use
timestamped listening events only.
To load the deterministic demo catalog and listening history for the separate demo user:
PYTHONPATH=apps/api .venv/bin/python scripts/seed_demo.py
# Create the empty personal user for a clean database (safe to rerun)
PYTHONPATH=apps/api .venv/bin/python scripts/bootstrap_personal.pyNetEase Cloud Music playlist URLs are detected in the web UI, but the public page currently does not expose the complete track list through a stable supported endpoint. MusicScope reports that limitation and keeps CSV import as the reliable path; see docs/NETEASE_IMPORT.md.
For the graduation demonstration, the supported real-data path is to open the
NetEase playlist in the documented third-party helper, copy its plain
Track - Artist lines, and paste them into the assisted import. This path is
designed for lists of roughly 2,500 tracks, preserves Unicode and version
names, keeps clearly delimited featured artists as separate relationships, and
does not create listening events or timestamps. Repeating the same import is
safe for library membership; raw import batches remain as provenance.
MusicScope has two intentionally separate modes. The PERSONAL user contains only imported personal library/history, does not invent a Timeline without timestamps, and does not substitute fictional concerts. The DEMO user contains deterministic catalog/history for demonstrating Timeline and recommendation behavior. They are never merged.
The profile API keeps long-term and short-term state separate. Long-term state uses all effective historical events plus library membership. Short-term state uses a configurable 30-day window with a 14-day exponential recency half-life and remains empty when only playlist membership is available. When as_of is omitted, the latest effective event is used as the reference time, which keeps the deterministic demo reproducible.
GET /api/v1/profile calculates a read-only view and does not persist a snapshot. Deliberate snapshot persistence uses POST /api/v1/profile/snapshots. Recommendations are generated through GET /api/v1/recommendations?exploration_level=50, with feedback posted to /api/v1/recommendations/{id}/feedback.
The evidence-first Timeline is generated deliberately with POST /api/v1/timeline/recalculate. It compares yearly genre/artist shares, new-artist rate, completion, skips, and listening frequency using a configurable change threshold. GET /api/v1/timeline is read-only. Music Memories are user-authored and managed independently through /api/v1/memories.
Recommendations are currently personalized library rediscovery and ranking, not unrestricted internet-wide music discovery. Exploration changes ranking, novelty, roles, and diversity among the user's available catalog; missing genre metadata is left missing rather than fabricated.
Concert discovery is deliberately lightweight and user-scoped. Demo fixtures
are available only through explicit demo mode. Normal personal results use
effective personal relationships and never silently fall back to fictional
events. To enable optional live Ticketmaster Discovery lookups, set
CONCERT_PROVIDER=ticketmaster and provide TICKETMASTER_API_KEY in .env.
The API endpoints are GET /api/v1/concerts for relevant artists and
GET /api/v1/artists/{artist_id}/concerts for one artist. MusicScope only
links to external event details; it does not sell tickets or process checkout.
If an older local database was seeded before personal/demo separation, inspect the targeted cleanup with:
PYTHONPATH=apps/api .venv/bin/python scripts/reset_personal_demo_contamination.pyRun it with --apply only after reviewing the dry-run counts. It targets the
known deterministic demo artist names, demo metadata, and deterministic-demo
import batch under the personal user; it does not wipe the database or
legitimate non-demo personal records.
To run the full containerized foundation instead:
docker compose up --buildmake api-test
make api-lint
make web-lint
make web-testThe database health endpoint is /health/db; it requires PostgreSQL to be running. Recommendations, timeline, memories, concerts, and cached audio separation are available in the MVP. External concert data remains optional.