# How the SpotifySaver CLI Explain Mode Shows Score Breakdown Without Downloading Tracks

> Learn how SpotifySaver CLI explain mode shows score breakdowns without downloading tracks. Discover detailed scoring components during dry-run analysis.

- Repository: [Gabriel Baute/spotify-saver](https://github.com/gabrielbaute/spotify-saver)
- Tags: internals
- Published: 2026-03-02

---

**The SpotifySaver CLI uses the `--explain` flag to perform a dry-run analysis that searches YouTube Music candidates and displays detailed scoring components for each potential match without executing any download logic or writing files to disk.**

The `gabrielbaute/spotify-saver` repository provides a Python-based command-line tool for converting Spotify tracks to local audio files via YouTube Music sources. When matches seem incorrect or users want to audit the selection algorithm, the **explain mode** offers a transparent view of the scoring mechanism. This feature leverages the `ScoreMatchCalculator` class to expose how duration, artist overlap, title similarity, and album metadata contribute to the final match score while completely bypassing file I/O operations.

## How the Explain Mode Works

Explain mode operates as a **search-only diagnostic tool** that intercepts the standard download flow before any audio retrieval or file writing occurs.

### CLI Flag Declaration and Propagation

The entry point for this functionality begins in the download command definition. In [`spotifysaver/cli/commands/download/download.py`](https://github.com/gabrielbaute/spotify-saver/blob/main/spotifysaver/cli/commands/download/download.py), the CLI declares the boolean flag:

```python
@click.option("--explain", is_flag=True,
              help="Show score breakdown for each track without downloading (for error analysis)")

```

When users invoke the command with this flag, the parameter propagates through the processing hierarchy. Whether handling a single track, album, or playlist, the `explain` boolean passes down to the respective `process_*` functions, ultimately reaching `process_track` in [`spotifysaver/cli/commands/download/track.py`](https://github.com/gabrielbaute/spotify-saver/blob/main/spotifysaver/cli/commands/download/track.py).

### Candidate Retrieval and Scoring

Upon entering explain mode, the application switches from the standard "select-best-and-download" flow to a diagnostic display. The `process_track` function instantiates a `ScoreMatchCalculator` and calls `searcher.search_raw(track)`, which returns **all raw candidate results** from YouTube Music rather than a single best match.

For each candidate result, the CLI invokes:

```python
explanation = scorer.explain_score(result, track, strict=True)

```

This method, implemented in [`spotifysaver/services/score_match_calculator.py`](https://github.com/gabrielbaute/spotify-saver/blob/main/spotifysaver/services/score_match_calculator.py), computes individual component scores including **duration similarity**, **artist overlap**, **title similarity**, and **album bonus**. The method returns a dictionary containing these discrete values plus the aggregated total score.

### Human-Readable Output and Early Exit

The CLI renders the score breakdown in a structured format:

```

- Candidate: <yt_title>
  Video ID: <yt_videoId>
  Duration: <duration_score>
  Artist:   <artist_score>
  Title:    <title_score>
  Album:    <album_bonus>
  → Total:  <total_score> (passed: <passed>)

```

After displaying all candidates, the code identifies the best match using:

```python
best = max(results, key=lambda r: scorer.explain_score(r, track)["total_score"])

```

Crucially, the function returns immediately after printing these explanations, bypassing the `downloader.download_track_cli` call and all file-writing operations. This early exit at lines 65-66 of [`track.py`](https://github.com/gabrielbaute/spotify-saver/blob/main/track.py) ensures **zero bytes are written to disk** and no audio streams are fetched.

## Running Explain Mode from the Command Line

To analyze a specific Spotify track's matching logic, execute:

```bash
spotify-saver download https://open.spotify.com/track/5K4W6fqB6G1e6pX5K6Zp8Y --explain

```

The output displays each YouTube candidate with component scores:

```

🔍 Explaining matches for track: Never Gonna Give You Up
🎵 Track: Never Gonna Give You Up
  - Candidate: Rick Astley – Never Gonna Give You Up (Official Music Video)
    Video ID: dQw4w9WgXcQ
    Duration: 0.3
    Artist:   0.3
    Title:    0.28
    Album:    0.1
    → Total:  0.98 (passed: True)
----------------------------------------
✅ Best candidate: Rick Astley – Never Gonna Give You Up (Official Music Video) (score: 0.98)

```

The command terminates after displaying the analysis without creating any audio files or metadata directories.

## Implementing Explain Logic in Custom Scripts

Developers can replicate this analysis programmatically without invoking the CLI:

```python
from spotifysaver.services import YoutubeMusicSearcher, ScoreMatchCalculator
from spotifysaver.services import SpotifyAPI

spotify = SpotifyAPI()
searcher = YoutubeMusicSearcher()
track = spotify.get_track("https://open.spotify.com/track/5K4W6fqB6G1e6pX5K6Zp8Y")

candidates = searcher.search_raw(track)
scorer = ScoreMatchCalculator()

for cand in candidates:
    expl = scorer.explain_score(cand, track, strict=True)
    print(f"{expl['yt_title']} – total score {expl['total_score']:.2f}")

```

This snippet accesses the same `search_raw` and `explain_score` methods used by the CLI's explain mode, enabling integration into custom debugging workflows or automated quality assurance pipelines.

## Summary

- The `--explain` flag triggers a dry-run analysis in [`spotifysaver/cli/commands/download/track.py`](https://github.com/gabrielbaute/spotify-saver/blob/main/spotifysaver/cli/commands/download/track.py) that displays match scoring without executing downloads.
- `ScoreMatchCalculator.explain_score()` computes component scores for duration, artist, title, and album metadata, returning a detailed breakdown dictionary.
- The `search_raw()` method fetches all YouTube Music candidates rather than selecting a single best match, enabling comparative analysis across multiple videos.
- Early return statements in the track processing logic prevent any file I/O or audio conversion from occurring.
- Both CLI users and Python developers can leverage this functionality to debug mismatches, understand ranking decisions, or refine scoring thresholds without consuming storage bandwidth.

## Frequently Asked Questions

### What scoring components does the explain mode display?

The explain mode displays four primary scoring components: duration similarity, artist overlap percentage, title string similarity, and an album match bonus. Each component contributes to a total score between 0 and 1, with the `passed` boolean indicating whether the candidate meets the strict matching threshold defined in the scorer configuration.

### Does explain mode consume YouTube API quota?

Yes, explain mode executes live searches against YouTube Music via the `search_raw` method, which consumes API quota units. However, it avoids the additional quota and bandwidth costs associated with audio stream analysis or file downloads since it stops after the search and scoring phase.

### Can I use explain mode with playlists and albums?

Yes, the `--explain` flag propagates through the processing hierarchy for albums and playlists. When processing these collections, the flag passes to each individual track's processing logic in sequence, displaying score breakdowns for every track in the collection without downloading any files.

### How does explain mode prevent actual downloads?

The `process_track` function in [`spotifysaver/cli/commands/download/track.py`](https://github.com/gabrielbaute/spotify-saver/blob/main/spotifysaver/cli/commands/download/track.py) contains explicit return statements immediately after printing the score explanations. This exits the function before reaching the `downloader.download_track_cli` invocation and file-writing logic, ensuring no audio files or metadata are persisted to storage.