Skip to content
Divyansh-KambojPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Animo

Music discovery backend for Animo. Users answer a short survey (seed artists, a vibe, a niche slider, a few preferred genres) and get back a small pack of tracks that lean toward the deeper, less-crowded corners of YouTube Music. Every track is enriched with Spotify metadata and carries an AI-written "living description" that keeps evolving as people comment on it.

Beyond discovery the service also backs vouching, comments and reactions, binders (9-slot collections), friends, and song sends.

Runs as a plain FastAPI app for local development and as an Azure Functions v2 ASGI wrapper in production (function_app.py wraps the same ASGI app).

What it does

  • POST /open-pack runs the discovery engine for the current user. Given survey inputs plus their prior liked-genre history, it walks the YouTube Music related-tracks graph, filters by a view-count window (the niche slider controls how mainstream that window is), enriches every hit through Spotify, and writes the tracks back to Supabase. Discovery and enrichment fan out across a thread pool so a five-track pack usually lands in about a second instead of five.
  • POST /vouch/{track_id} records a per-user vouch (one per user per track, idempotent) and merges the track's genre tags into that user's favorite genres so future packs get smarter.
  • POST /comment saves a comment on a track. Every fifth comment triggers a background Azure OpenAI call that rewrites the track's people-voice description from the accumulated community reactions.
  • GET /search-global and POST /search-global/select let a user pull any YouTube Music track into the archive on demand.

Auth is handled with Supabase JWTs, verified against the project's JWKS endpoint (with a manual cache-bust on key rotation) or the legacy HS256 shared secret depending on the token's alg.

Stack

  • FastAPI, Python 3.11+
  • Supabase (Postgres + auth)
  • ytmusicapi + spotipy — discovery and enrichment
  • Azure OpenAI — the two-voice living descriptions
  • colorthief + Pillow — album-art palette extraction

Setup

Requires a Supabase project with the migrations in supabase/migrations/ applied (in numeric order), a Spotify API app, an Azure OpenAI deployment, and — only if you want to run the Functions host locally — Azure Functions Core Tools v4.

python3.11 -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate

pip install -r requirements.txt -r requirements-dev.txt
cp .env.template .env              # then fill it in

Environment

Names only — see .env.template. Never commit real values.

Variable Purpose
SUPABASE_URL Project URL
SUPABASE_KEY Anon key
SUPABASE_SERVICE_ROLE_KEY Service-role key (server-side only)
SUPABASE_JWT_SECRET JWT secret, for symmetric token verification
SPOTIFY_CLIENT_ID / SPOTIFY_CLIENT_SECRET Spotify enrichment
AZURE_OPENAI_API_KEY / AZURE_OPENAI_ENDPOINT / AZURE_OPENAI_DEPLOYMENT_NAME Living descriptions
GOOGLE_CLIENT_ID Google OAuth
ANIMO_ALLOWED_ORIGINS Comma-separated CORS origins. Defaults to localhost dev ports; set this in production
ANIMO_LOG_LEVEL Log level, defaults to INFO

Running

uvicorn main:app --reload      # http://localhost:8000, docs at /docs
func start                     # Azure Functions host, http://localhost:7071

Tests

pytest

179 unit tests over the pure helpers (streaks, binder slot validation, taste-twin scoring, voice normalization, reaction aggregation, friendship status, song sends, first-finder credit, palette). Tests marked integration hit the live network and are excluded by default — see pytest.ini.

Endpoints

Auth required unless marked public.

Health & profile

Method Path Description
GET / Root ping (public)
GET /health Health check with database reachability; 503 when the database is down (public)
GET /me Profile, streak, survey state, unopened send count

Discovery & tracks

Method Path Description
POST /open-pack Run the discovery engine and return today's enriched pack
GET /my-tracks The user's library, newest first, with first-finder credit and perceptions
GET /tracks/{track_id} One track (public)
GET /tracks/{track_id}/history People-voice description history, newest first
GET /search-global Global YouTube Music search, top 5 matches (public)
POST /search-global/select Resolve a search result into a full Animo card

Comments & vouches

Method Path Description
GET /comments/{track_id} 50 most recent comments with reaction counts (public)
POST /comment Add a comment; every 5th regenerates the people-voice
POST /comments/{comment_id}/react Toggle one of: fire, heart, laugh, skull, eyes
GET /vouches Track IDs the user has vouched for
POST /vouch/{track_id} Idempotent vouch, one per user per track

Friends

Method Path Description
GET /friends Accepted friends with profile info and taste-twin flag
GET /friends/requests Incoming pending requests
POST /friends/request/{target_user_id} Send a request
POST /friends/accept/{friendship_id} Accept
POST /friends/decline/{friendship_id} Decline
DELETE /friends/{friendship_id} Unfriend; either party may delete
GET /users/search Search users by email prefix
GET /users/{target_user_id}/profile Public profile

Sends & conversations

Method Path Description
POST /sends Send a track to a friend, with an optional note
GET /conversations Per-partner conversation summaries, most recent first
GET /conversations/{other_user_id} One conversation thread
POST /sends/{send_id}/open Mark opened; recipient only, idempotent
POST /sends/{send_id}/add Copy the track into the recipient's library
POST /sends/{send_id}/pass Mark passed; recipient only

Binders

Binders are 9-slot collections.

Method Path Description
POST /binders Create
GET /binders/{binder_id} One binder with slot tracks (public)
PATCH /binders/{binder_id} Update title/description/cover mode; owner only
DELETE /binders/{binder_id} Delete, cascading to slots and vouches; owner only
PUT /binders/{binder_id}/slots Set/move tracks between slots atomically; owner only
GET /users/{target_user_id}/binders A user's binders
POST /binders/{binder_id}/vouch Idempotent binder vouch

Layout

  • main.py FastAPI app, auth middleware, routes
  • discovery.py YouTube Music niche crawler
  • metadata.py Spotify enrichment (spotipy)
  • agent.py Azure OpenAI two-voice description generator
  • database.py Supabase data layer
  • palette.py album-art palette extraction
  • streaks.py pure streak calculation
  • function_app.py Azure Functions v2 ASGI wrapper
  • api/routes/ route modules (health check, etc.)
  • supabase/migrations/ schema migrations, applied in numeric order
  • tests/ unit tests

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages