Files
MusicAnalyser/backend/TECHNICAL_DOCS.md
2025-12-25 22:17:21 +04:00

5.6 KiB
Raw Blame History

Technical Documentation: Stats & Narrative Services

Overview

This document details the implementation of the core analysis engine (StatsService) and the AI narration layer (NarrativeService). These services transform raw Spotify listening data into computable metrics and human-readable insights.

1. StatsService (backend/app/services/stats_service.py)

The StatsService is a deterministic calculation engine. It takes a time range (period_start to period_end) and aggregates PlayHistory records.

Core Architecture

  • Input: SQLAlchemy Session, Start Datetime, End Datetime.
  • Output: A structured JSON dictionary containing discrete analysis blocks (Volume, Time, Sessions, Vibe, etc.).
  • Optimization: Uses joinedload to eagerly fetch Track and Artist relations, preventing N+1 query performance issues during iteration.

Metric Logic

A. Volume & Consumption

  • Top Tracks/Artists: Aggregated by ID, not name, to handle artist renames or duplicates.
  • Concentration Metrics:
    • HHI (HerfindahlHirschman Index): Measures diversity. SUM(share^2). Close to 0 = diverse, close to 1 = repetitive.
    • Gini Coefficient: Measures inequality of play distribution.
    • Genre Entropy: -SUM(p * log(p)) for genre probabilities. Higher = more diverse genre consumption.
  • Artists: Parsed from the Track.artists relationship (Many-to-Many) rather than the flat string, ensuring accurate counts for collaborations (e.g., "Drake, Future" counts for both).

B. Time & Habits

  • Part of Day: Fixed buckets:
    • Morning: 06:00 - 12:00
    • Afternoon: 12:00 - 18:00
    • Evening: 18:00 - 23:59
    • Night: 00:00 - 06:00
  • Streaks: Calculates consecutive days with at least one play.
  • Active Days: Count of unique dates with activity.

C. Session Analytics

  • Session Definition: A sequence of plays where the gap between any two consecutive tracks is ≤ 20 minutes. A gap > 20 minutes starts a new session.
  • Energy Arcs: Compares the energy feature of the first and last track in a session.
    • Rising: Delta > +0.1
    • Falling: Delta < -0.1
    • Flat: Otherwise

D. The "Vibe" (Audio Features)

  • Aggregation: Calculates Mean, Standard Deviation, and Percentiles (P10, P50/Median, P90) for all Spotify audio features (Energy, Valence, Danceability, etc.).
  • Whiplash Score: Measures the "volatility" of a listening session. Calculated as the average absolute difference in a feature (Tempo, Energy, Valence) between consecutive tracks.
    • High Whiplash (> 15-20 for BPM) = Chaotic playlist shuffling.
    • Low Whiplash = Smooth transitions.
  • Profiles:
    • Mood Quadrant: (Avg Valence, Avg Energy) coordinates.
    • Texture: Acousticness vs. Instrumentalness.

E. Context & Behavior

  • Context URI: Parsed to determine source (Playlist vs. Album vs. Artist).
  • Context Switching: Percentage of track transitions where the context_uri changes. High rate = user is jumping between playlists or albums frequently.

F. Lifecycle & Discovery

  • Discovery: Tracks played in the current period that were never played before period_start.
  • Obsession: Tracks with ≥ 5 plays in the current period.
  • Skip Detection (Boredom Skips):
    • Logic: (next_start - current_start) < (current_duration - 10s)
    • Only counts if the listening time was > 30s (to filter accidental clicks).
    • Proxy for "User got bored and hit next."

2. NarrativeService (backend/app/services/narrative_service.py)

The NarrativeService acts as an interpreter. It feeds the raw JSON from StatsService into Google's Gemini LLM to generate text.

Payload Shaping

To ensure reliability and manage token costs, the service does not send the raw full database dump. It pre-processes the stats:

  • Truncates top lists to Top 5.
  • Removes raw transition arrays.
  • Simplifies nested structures.

LLM Prompt Engineering

The system uses a strict persona ("Witty Music Critic") and enforces specific constraints:

  • Output: Strict JSON.
  • Safety: Explicitly forbidden from making mental health diagnoses (e.g., no "You seem depressed").
  • Content: Must reference specific numbers from the input stats (e.g., "Your 85% Mainstream Score...").

Output Schema

The LLM returns a JSON object with:

  • vibe_check: 2-3 paragraph summary.
  • patterns: List of specific observations.
  • persona: A creative 2-3 word label (e.g., "The Genre Chameleon").
  • roast: A playful critique.
  • era_insight: Commentary on the user's "Musical Age" (weighted avg release year).

3. Data Models (backend/app/models.py)

  • Track: Stores static metadata and audio features.
    • lyrics: Full lyrics from Genius (Text).
    • image_url: Album art URL (String).
    • raw_data: The full Spotify JSON for future-proofing.
  • Artist: Normalized artist entities.
    • image_url: Artist profile image (String).
  • PlayHistory: The timeseries ledger. Links Track to a timestamp and context.
  • AnalysisSnapshot: Stores the final output of these services.
    • metrics_payload: The JSON output of StatsService.
    • narrative_report: The JSON output of NarrativeService.

4. External Integrations

Spotify

  • Ingestion: Polls recently-played endpoint every 60s.
  • Enrichment: Fetches Artist genres and images.

Genius

  • Client: backend/app/services/genius_client.py.
  • Function: Searches for lyrics and high-res album art if missing from Spotify data.
  • Trigger: Runs during the ingestion loop for new tracks.

ReccoBeats

  • Function: Fetches audio features (Danceability, Energy, Valence) for tracks.