{"id":50120178,"url":"https://github.com/sauravpanda/wcs-analyzer","last_synced_at":"2026-05-23T18:05:30.377Z","repository":{"id":349910418,"uuid":"1203447322","full_name":"sauravpanda/wcs-analyzer","owner":"sauravpanda","description":"AI-powered West Coast Swing dance video analyzer with WSDC-style scoring. Uses Gemini for native video+audio analysis.","archived":false,"fork":false,"pushed_at":"2026-04-15T19:51:24.000Z","size":219,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-15T21:20:19.411Z","etag":null,"topics":["ai","dance","gemini","video-analysis","wcs","west-coast-swing","wsdc"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sauravpanda.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-07T03:39:39.000Z","updated_at":"2026-04-15T19:51:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sauravpanda/wcs-analyzer","commit_stats":null,"previous_names":["sauravpanda/wcs-analyzer"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/sauravpanda/wcs-analyzer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sauravpanda%2Fwcs-analyzer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sauravpanda%2Fwcs-analyzer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sauravpanda%2Fwcs-analyzer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sauravpanda%2Fwcs-analyzer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sauravpanda","download_url":"https://codeload.github.com/sauravpanda/wcs-analyzer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sauravpanda%2Fwcs-analyzer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33406514,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T04:15:53.637Z","status":"ssl_error","status_checked_at":"2026-05-23T04:15:53.242Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","dance","gemini","video-analysis","wcs","west-coast-swing","wsdc"],"created_at":"2026-05-23T18:05:06.262Z","updated_at":"2026-05-23T18:05:30.356Z","avatar_url":"https://github.com/sauravpanda.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WCS Analyzer\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)\n[![CI](https://github.com/sauravpanda/wcs-analyzer/actions/workflows/ci.yml/badge.svg)](https://github.com/sauravpanda/wcs-analyzer/actions)\n\nAI-powered West Coast Swing dance video analyzer that evaluates performances according to WSDC competition scoring standards.\n\nDrop in a video of your WCS dancing, get back detailed scores, technique breakdowns, partner-specific feedback, and actionable improvement tips — like having a judge review your practice sessions.\n\n**Beyond LLM vibes.** Pose estimation (MediaPipe) can measure posture, extension, footwork, and slot linearity directly from the video, and a beat-sync verifier cross-correlates detected footfalls with librosa-detected beats to produce an *objective* timing score. Multi-provider ensemble mode runs several models on the same clip and flags categories where they disagreed. Longitudinal tracking answers \"am I getting better?\" across runs.\n\n## How It Works\n\n### Gemini (default) — Native Video + Audio\n\n```\nVideo File (.mp4, .mov, etc.)\n       |\n       v\n  Gemini API ────────\u003e Uploads full video with audio\n       |                Model SEES motion and HEARS the music\n       v\n  Scoring Engine ────\u003e WSDC-weighted aggregation\n       |\n       v\n  Report ────────────\u003e Terminal, JSON, or CSV output\n```\n\nGemini processes the entire video natively — it can see continuous movement and hear the music, so timing judgments are based on actual beat alignment, not guesswork from still frames.\n\n### Claude (fallback) — Frame-Based\n\n```\nVideo File\n       |\n       v\n  Frame Extraction ──\u003e Samples frames at configurable FPS (OpenCV)\n  Audio Processing ──\u003e Beat detection, BPM estimation (librosa)\n       |\n       v\n  Claude Vision ─────\u003e Analyzes each segment from still images\n       |\n       v\n  Scoring Engine ────\u003e WSDC-weighted aggregation\n       |\n       v\n  Report\n```\n\nClaude analyzes individual frames with beat context from audio analysis. Works well but can't see motion between frames or hear the music directly.\n\n### Why Gemini is the default\n\nFor WCS analysis, seeing motion is critical — you can't judge timing, connection, or slot maintenance from snapshots. Gemini is the only major AI API that accepts video files directly with audio, making it far better suited for dance evaluation.\n\n## Scoring Categories\n\n| Category | Weight | What It Evaluates |\n|---|---|---|\n| **Timing \u0026 Rhythm** | 30% | On-beat dancing, syncopation accuracy, anchor steps, rhythm consistency |\n| **Technique** | 30% | Posture, extension, footwork, slot maintenance, connection frame |\n| **Teamwork** | 20% | Lead/follow connection, shared weight, responsiveness, matched energy |\n| **Presentation** | 20% | Musicality, styling, confidence, performance quality |\n\nScores are 1-10 per category, with letter grades from A+ to F. Lead and follow get individual technique and presentation scores.\n\nEvery score now comes with a **confidence interval** (`score_low` / `score_high`). Wide intervals mean the model was uncertain — a shaky camera angle, ambiguous technique, or a cropped frame. The report shows the interval next to the score and raises a ⚠ *low confidence* warning when any category's range exceeds 2.0 points. Scores are also **clamped to [1, 10]** on parse, and the Claude path automatically **retries once with a corrective hint** if the response can't be parsed as JSON — so a malformed response never silently collapses to 5.0s.\n\nThe segment prompts require the model to fill a **`reasoning` field before each score**, a lightweight chain-of-thought pattern that improves rubric-grading consistency. Three few-shot exemplars (novice, intermediate, champion) are embedded in the system prompt to anchor the 1-10 scale.\n\n## Quick Start\n\n### Prerequisites\n\n- Python 3.10+\n- [ffmpeg](https://ffmpeg.org/download.html) installed and on your PATH\n- A [Google Gemini API key](https://aistudio.google.com/apikey) (default) and/or an [Anthropic API key](https://console.anthropic.com/) (for Claude fallback)\n\n### Install\n\n```bash\npip install -e .\n\n# Optional: MediaPipe for pose-driven metrics (see --pose below)\npip install -e \".[pose]\"\n```\n\n### Set your API key\n\n```bash\n# For Gemini (default provider)\nexport GEMINI_API_KEY=\"your-key-here\"\n\n# For Claude (fallback)\nexport ANTHROPIC_API_KEY=\"sk-ant-...\"\n```\n\n### Analyze a video\n\n```bash\nwcs-analyzer analyze my_dance.mp4\n```\n\nThat's it. You'll get a full terminal report with scores, technique breakdown, partner feedback, and improvement suggestions.\n\n## Commands\n\n### `analyze` — Full analysis\n\n```bash\nwcs-analyzer analyze video.mp4 [OPTIONS]\n```\n\n| Option | Default | Description |\n|---|---|---|\n| `--provider` | `gemini` | AI provider: `gemini` (native video+audio), `claude` (API, frame-based), or `claude-code` (local CLI) |\n| `--providers` | — | Comma-separated list for **ensemble mode** (e.g. `gemini,claude-code`). Each runs independently; results are aggregated with disagreement flagging |\n| `--model` | auto | Model ID (defaults to `gemini-3.1-pro-preview` or `claude-sonnet-4-6`) |\n| `--detail` | `medium` | Analysis granularity: `low`, `medium`, `high` |\n| `--fps` | `3.0` | Frames per second to sample — Claude only (1-30) |\n| `--dancers` | — | Describe which dancers to focus on (for crowded floors) |\n| `--pose` | — | Run MediaPipe pose estimation + beat-sync and feed the measured metrics to the LLM (Gemini only; needs `[pose]` extra) |\n| `--save-history` | — | Persist this run's scores under the given dancer name for longitudinal tracking |\n| `--format` | `terminal` | Output format: `terminal`, `json`, `csv` |\n| `-o`, `--output` | — | Save JSON report to a file path |\n| `-j`, `--parallel` | `1` | Number of videos to analyze in parallel (batch mode) |\n| `--no-cache` | — | Force re-analysis, skip cache |\n| `-v`, `--verbose` | — | Enable debug logging |\n\n**Examples:**\n\n```bash\n# Default: Gemini with native video analysis\nwcs-analyzer analyze competition.mp4\n\n# Focus on a specific couple on a crowded floor\nwcs-analyzer analyze competition.mp4 --dancers \"lead in blue shirt, follow in red dress\"\n\n# Use Claude instead\nwcs-analyzer analyze competition.mp4 --provider claude\n\n# High detail with Gemini 3 Pro (default) — richer, slower, pricier\nwcs-analyzer analyze competition.mp4 --detail high\n\n# Or explicitly pin the older, cheaper Gemini 2.5 Flash\nwcs-analyzer analyze competition.mp4 --model gemini-2.5-flash\n\n# Export as CSV\nwcs-analyzer analyze competition.mp4 --format csv -o scores.csv\n\n# Pose-driven objective metrics (MediaPipe) + beat-sync verification\nwcs-analyzer analyze competition.mp4 --pose\n\n# Ensemble: run two providers and flag categories where they disagreed\nwcs-analyzer analyze competition.mp4 --providers gemini,claude-code\n\n# Save this run to the longitudinal history under \"alice\"\nwcs-analyzer analyze competition.mp4 --save-history alice\n```\n\n### `timing` — Quick timing check\n\nA faster, cheaper analysis focused only on beat alignment:\n\n```bash\nwcs-analyzer timing video.mp4\nwcs-analyzer timing video.mp4 --provider claude\n```\n\n### `compare` — Compare multiple analyses\n\nCompare scores across multiple JSON reports to track progress:\n\n```bash\nwcs-analyzer compare session1.json session2.json session3.json\n```\n\nShows side-by-side scores with trend indicators.\n\n### `patterns` — WCS pattern timeline\n\nDetect and list the patterns (sugar push, whip, side pass, tuck turn, …) on the video's timeline in a single cheap LLM call. Useful as a sanity check before a full analysis.\n\n```bash\nwcs-analyzer patterns video.mp4\n```\n\nRenders a colored table of pattern segments with start/end times, duration, and per-pattern confidence (green/yellow/red).\n\n### `progress` — Longitudinal dancer tracking\n\nReads the history DB populated by `analyze --save-history \u003cname\u003e` and renders a run timeline plus linear-fit trajectories per category:\n\n```bash\nwcs-analyzer analyze jan.mp4 --save-history alice\nwcs-analyzer analyze feb.mp4 --save-history alice\nwcs-analyzer analyze mar.mp4 --save-history alice\n\nwcs-analyzer progress alice\n```\n\nEach trajectory is labeled **improving**, **flat**, or **regressing** based on the per-run slope. History lives at `~/.wcs-analyzer/history.db` (override with `WCS_ANALYZER_HISTORY_DB`).\n\n### `dancers` — List tracked dancers\n\n```bash\nwcs-analyzer dancers\n```\n\nShows every dancer in the longitudinal history store with their run count.\n\n## Output\n\n### Terminal Report\n\nThe default terminal output includes:\n- Overall score with letter grade\n- Category score table with visual bars\n- Technique sub-scores (posture, extension, footwork, slot)\n- Partner breakdown (lead vs follow scores)\n- Off-beat moment timeline\n- Identified patterns (sugar push, whip, etc.)\n- Top strengths and areas to improve\n- Judge's notes\n\n### JSON Export\n\n```bash\nwcs-analyzer analyze video.mp4 --format json -o report.json\n```\n\nStructured JSON with all scores, partner breakdown, and detailed feedback — useful for building dashboards or tracking progress programmatically.\n\n### CSV Export\n\n```bash\nwcs-analyzer analyze video.mp4 --format csv\n```\n\nSpreadsheet-friendly format with summary rows and per-segment scores.\n\n## Advanced Features\n\n### Pose-driven objective metrics (`--pose`)\n\nTechnique sub-scores (posture, extension, footwork, slot) are normally inferred by the LLM from still frames. With `--pose` and the `[pose]` extra installed, the tool runs MediaPipe BlazePose over the video to compute:\n\n- **Posture** — spine deviation from vertical, mean and stddev across frames\n- **Extension** — arm-to-torso length ratio (mean and peak)\n- **Footwork** — footfall rate from ankle-y zero-crossings\n- **Slot** — hip-trajectory linearity as R², via PCA on the 2×2 covariance matrix (works for any line direction)\n\nThe measured values are formatted as a context block and prepended to the Gemini prompt, with an explicit instruction to the model to treat them as objective measurements. **Known limitation:** BlazePose is single-person, so the metrics reflect whichever dancer the detector locked onto — typically the lead. Multi-person tracking is a future improvement.\n\n### Beat-sync verification (pairs with `--pose`)\n\nWhen `--pose` is enabled, the tool also cross-correlates pose-detected footfalls with librosa-detected audio beats and computes an **objective timing score** in `[0, 10]`:\n\n- 10 = every footfall lands exactly on a beat\n- 0 = mean offset is half a beat interval (on the off-beat)\n\nThe result is surfaced in the prompt with the instruction: *keep `timing.score` within ±1.0 of the measured value unless there's a strong musical-interpretation reason, and explain any deviation in `timing.reasoning`*. This is the \"measured timing, LLM-judged artistry\" pattern.\n\n### Ensemble mode (`--providers`)\n\nRun multiple providers on the same clip and aggregate:\n\n```bash\nwcs-analyzer analyze video.mp4 --providers gemini,claude-code\n```\n\n- Consensus is computed as the **median** across providers per category (more robust to a single wild outlier than the mean)\n- Per-category stddev is reported; any category where stddev \u003e 1.0 is flagged as ⚠ **contested** — a signal that the models disagreed and a human should review\n- Graceful degradation: if a provider errors out, the ensemble continues with the rest (requires at least two successful runs)\n\n## Caching\n\nResults are automatically cached in `~/.wcs-analyzer/cache/` based on a hash of the video file, provider, and analysis parameters. Re-running the same video with the same settings will use the cached result instantly.\n\n```bash\n# Force a fresh analysis\nwcs-analyzer analyze video.mp4 --no-cache\n```\n\n## Architecture\n\n```\nsrc/wcs_analyzer/\n  cli.py                  CLI — analyze, timing, compare, patterns,\n                          progress, dancers commands\n  gemini_analyzer.py      Gemini native video+audio analysis\n  analyzer.py             Claude frame-based analysis, JSON parse + retry,\n                          score clamping, shared parse_segment_data helper,\n                          pattern-timeline detection\n  claude_code_analyzer.py Local Claude Code CLI provider\n  video.py                OpenCV frame extraction and phrase grouping\n  audio.py                ffmpeg audio extraction + librosa beat detection\n  pose.py                 MediaPipe BlazePose extraction + posture/\n                          extension/footwork/slot metrics + beat-sync\n                          verification (optional [pose] extra)\n  history.py              SQLite longitudinal tracking + linear-fit\n                          trajectories per category\n  prompts.py              WSDC prompts with few-shot exemplars,\n                          chain-of-thought reasoning fields, and\n                          confidence-interval schema\n  scoring.py              Weighted score aggregation, confidence-interval\n                          aggregation, ensemble median + disagreement\n                          flagging, grade assignment\n  report.py               Rich terminal output + JSON/CSV export, ensemble\n                          report, progress report\n  cache.py                File-hash based LLM response caching\n  exceptions.py           Custom exception hierarchy\n```\n\n## Development\n\n### Setup\n\n```bash\n# Install with dev dependencies\npip install -e \".[dev]\"\n\n# Or with uv\nuv sync\n```\n\n### Run checks\n\n```bash\nruff check src/ tests/    # Lint\npyright src/               # Type check\npytest tests/ -v           # Tests (156+ tests, all mocked, no API key needed)\n```\n\n### Run all CI checks locally\n\n```bash\nruff check src/ \u0026\u0026 pyright src/ \u0026\u0026 pytest tests/ -v\n```\n\n## Tips for Best Results\n\n- **Video length**: 30 seconds to 5 minutes works best. Trim to the dance portion.\n- **Camera angle**: A wide shot showing both partners' full bodies gives the best analysis. Pose estimation (`--pose`) needs a reasonably unobstructed view.\n- **Audio quality**: Clear music helps — Gemini hears it directly, Claude relies on librosa beat detection.\n- **Detail level**: Use `--detail low` for quick feedback, `--detail high` for thorough analysis (costs more).\n- **Provider**: Use Gemini (default) for best results. Fall back to Claude if you don't have a Gemini API key.\n- **Objective technique scoring**: Add `--pose` when technique really matters — spine angles, extension ratios, footfall rate, and slot linearity become measured instead of inferred.\n- **Disagreement check**: Use `--providers gemini,claude-code` on contested or edge-case routines. Categories with stddev \u003e 1.0 are flagged for human review.\n- **Progress tracking**: Always pass `--save-history \u003cyour-name\u003e` so `wcs-analyzer progress \u003cyour-name\u003e` can render your trajectory over time.\n\n## Tech Stack\n\n- **[Google Gemini](https://ai.google.dev/)** — Native video + audio understanding (default provider)\n- **[Anthropic Claude](https://www.anthropic.com/)** — Vision model for frame-based analysis\n- **[MediaPipe](https://developers.google.com/mediapipe)** — BlazePose landmark detection for objective technique metrics (optional)\n- **[OpenCV](https://opencv.org/)** — Video frame extraction\n- **[librosa](https://librosa.org/)** — Audio beat detection and BPM estimation\n- **[SQLite](https://www.sqlite.org/)** — Longitudinal history store (stdlib)\n- **[Click](https://click.palletsprojects.com/)** — CLI framework\n- **[Rich](https://rich.readthedocs.io/)** — Terminal formatting\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsauravpanda%2Fwcs-analyzer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsauravpanda%2Fwcs-analyzer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsauravpanda%2Fwcs-analyzer/lists"}