Metadata-Version: 2.4
Name: mamonia
Version: 0.1.0
Summary: Local-first music discovery CLI inspired by Prawo Mamonia.
Author-email: Kajetan Jagiełka <heartless-jeden@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://heartless.one/
Project-URL: Repository, https://github.com/heartless-jeden/mamonia
Project-URL: Issues, https://github.com/heartless-jeden/mamonia/issues
Keywords: music,discovery,spotify,cli,musicbrainz,lastfm
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Topic :: Multimedia :: Sound/Audio
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer>=0.12
Requires-Dist: pydantic>=2.7
Requires-Dist: httpx>=0.27
Requires-Dist: spotipy>=2.23
Requires-Dist: python-dotenv>=1.0
Requires-Dist: questionary>=2.0
Requires-Dist: rapidfuzz>=3.0
Requires-Dist: rich>=13.0
Requires-Dist: numpy>=1.26
Requires-Dist: pandas>=2.2
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-xdist>=3.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Provides-Extra: tidal
Requires-Dist: tidalapi>=0.8.0; extra == "tidal"
Provides-Extra: csvtool
Requires-Dist: questionary>=2.0; extra == "csvtool"
Dynamic: license-file

# Mamonia

A music discovery CLI tool with an intuitive interface that keeps it local. 
Inspired by Engineer Mamoń's Principle / Prawo inżyniera Mamonia.

It starts from playlists and libraries you already like, builds a reusable profile of your taste, then explores artist networks and music databases to find fresh releases you might dig. 
It remembers what it's already recommended so you don't get the same suggestions twice. Never ever.

Think of it as an extension of Spotify's algorithm - instead of a black box, influenced by $, doing its thing, Mamonia walks you through the connections between artists and pulls recommendations from MusicBrainz, Last.fm, ListenBrainz, and Deezer when useful. 
Spotify's just there acting as a catalog and to help fill in metadata gaps.

## Current Features

- Guided interactive menu: run `mamonia --help` to see all commands, or run bare `mamonia` for the guided interactive menu.
- Guided localization with English and Polish locale files, plus a saved language preference for the menu flow.
- Guided profile reuse decisions based on real playlist delta and enrichment completeness.
- Durable SQLite artist/track enrichment reused across runs and saved profiles.
- Guided library maintenance for heard releases, cache management, saved profile backup/export/import, and Spotify enrichment refresh.
- Supported inputs: 
  - preferred: Spotify playlist URL / ID  or TuneMyMusic-style `.csv`,    
  - also viable: Tidal playlist URL/ID or a simple `.txt`.
- Outputs: TuneMyMusic-style `.csv`, plain `.txt`, and JSON.
- Private Spotify playlist creation as the default guided output, with a local CSV backup always written alongside it. Spotify open-URL link-back included in exports when Spotify data is present.
- Reusable source profiles keyed by playlist ID or order-independent text/CSV track-set hash.
- Delta updates for changed playlists: Mamonia detects additions/removals, asks before updating, and reuses existing data.
- Global memory mode to exclude known tracks from every saved profile, not only the active source.
- Input playlist releases are automatically marked as heard before discovery begins, preventing recommendations from the user's existing collection.
- Permanent release-level heard list; successful exports automatically mark releases as heard.
- Mandatory discovery progress with one updating Rich/ASCII block in interactive terminals and compact fallback lines in redirected output.
- Dry-run previews that do not write exports, mark heard releases, or create playlists.
- Provider-aware rate-limit warnings and `mamonia diagnose` for local state, cache stats, provider TTLs, and credential checks.
- SQLite API cache for provider data, including normalized cache keys, provider-specific TTLs, `--force-refresh`, Spotify artist/album/tracklist lookups, and negative Spotify misses.
- Variant-aware duplicate and heard matching for deluxe/remaster/regional/anniversary-style releases when stable IDs are missing.
- Cache-first Spotify track resolution with MusicBrainz tracklist fallback, Spotify track backfill, and conservative representative-track fallback.
- Hardened Spotify auth and request handling: PKCE for playlist read/write, client credentials for catalog lookups, scope-specific token caches, bounded retry for required playlist operations, and clearer auth/scope/market/access error messages.
- Discovery scoring improvements including serendipity weighting, `--discovery-mode`, and label-affinity candidates from MusicBrainz.
- JSON explainability fields that summarize candidate source, provider evidence, filter/fallback context, and tracklist derivation.
- CSV/JSON track diagnostics including `Spotify URI`, `Spotify URL`, `Spotify Attribution`, `Resolution source`, and `Resolution note`.
- Tidal feasibility diagnostics in `mamonia diagnose`, plus experimental native Tidal playlist creation (`mamonia tidal-playlists`, `mamonia tidal-smoke`).

### Human Logic Proposal Pipeline

- Tier-driven discovery (Tier 1–4): seed artists → MusicBrainz collaborators → ListenBrainz similarity → Last.fm similarity.
- Structured recommendation explanations (seed connections, confidence, matching tags).
- Balanced v2 selection defaults to a high/medium/low confidence mix with diversity pressure; `top_first` remains available for strict score ordering.
- Optional v2 controls can penalize artists with many recent EPs/singles and deterministically shuffle whole release recommendations.
- Scoring with multi-seed diminishing returns (100%/70%/40%/20%) and modifiers for reissues, uncertainty, and tag-only matches.
- Track enrichment pipeline (Spotify → MusicBrainz → ISRC fallback via Tidal/Deezer).
- Enrichment phase separation:
  - discovery-time candidate browsing uses lightweight enrichment only (MusicBrainz + ISRC fallback),
  - full enrichment (including Spotify lookup/backfill/recovery) runs only after final release candidates are selected.
- Cross-recommendation track deduplication: ISRC and Spotify ID exact matches removed automatically; borderline fuzzy matches (50–74%) presented for interactive review; the more-complete track absorbs metadata from the duplicate before discard.

Mamonia deliberately does not use Spotify Audio Features, Audio Analysis, Recommendations, or Related Artists.

## How It Works: The Simple Version

**Building your taste profile:**

You give Mamonia a Spotify playlist, Tidal playlist, or a simple text/CSV file of songs you like. Mamonia reads all the artists and looks them up in four music databases:
- **Spotify** — what else are they famous for
- **MusicBrainz** — who do they collaborate with, which bands are related
- **Last.fm** — what tags do listeners use, who else listens to them
- **ListenBrainz** — how many listeners share taste (which ones also listen to other artists)

All this info gets stored locally in a file called a "taste profile" — essentially a map that says "you like these artists, they're connected to these other artists, they make music tagged as rock/electronic/etc., and they've worked with these collaborators."

**Finding recommendations (default mode):**

When you ask for recommendations, Mamonia:

1. **Expands your network** — starts with your seed artists and follows the connections: "if you like Massive Attack, you might like their collaborators (Ghostpoet) or the artists they're similar to (Portishead)."

2. **Fetches releases** — looks up new albums/EPs from all those connected artists in MusicBrainz (the most comprehensive open music database). When `--use-streaming-providers` is enabled and MusicBrainz lacks results, Deezer and Tidal are queried as a fallback; independent execution ensures one provider's failure does not suppress the other, and corroborated releases are marked for higher confidence.

3. **Filters** — removes old stuff you asked for (e.g., last 6 months), removes albums already in your heard list, removes bad matches.

4. **Scores** — ranks remaining albums by how many of your seed artists are connected to them. An album by Ghostpoet scores high because Ghostpoet worked with Massive Attack (one of your seeds). A random artist with the same genre scores lower.

5. **Outputs** — gives you the top results as a CSV/JSON/TXT file, with links to Spotify (if the tracks were found there).

**Concrete example:**

You have a Spotify playlist: `Massive Attack`, `Portishead`, `Burial`.

Mamonia learns:
- Massive Attack worked with **Ghostpoet** and is related to **Portishead**
- Portishead is similar to **Burial**
- All three are tagged "trip-hop" or "electronic" in Last.fm

When you ask for recommendations, Mamonia finds:
- A new Ghostpoet album (Massive Attack collaborator) → scores high
- A new album by an artist Portishead's listeners also love → scores medium
- A random trip-hop artist from ListenBrainz with no other connection → scores low and is kept to the balanced-mode wildcard quota rather than filling the list.

Result: you get the Ghostpoet album first, and the high-confidence connections, not a random artist filling space.

**What makes it different from Spotify:**

- Spotify's algorithm is a black box. Mamonia shows you **why** it picked something.
- Spotify's algorithm may prioritize what makes them money. Mamonia just walks the graph of actual artist connections.
- You keep **full control** — all data lives on your machine. No tracking, no streaming history leaks, no ads.
- Results are **reproducible** — same input, same profile, same date range = same output. No surprises due to A/B tests.

## Documentation

### Unified Documentation Structure

This project maintains two documentation directories with a clear **hierarchy of truth**:

1. **Source Code** — The most up-to-date reference; code is canonical
2. **`docs/` Documentation** — Canonical documentation for all models and humans
3. **`.ai/` Documentation** — Active compatibility mirror of canonical docs

### For AI/Automation Mirrors

- [`.ai/README.md`](.ai/README.md) — Entry point for Claude documentation
- [`.ai/MEMORY.md`](.ai/MEMORY.md) — Current project state and constraints
- [`.ai/ARCHITECTURE.md`](.ai/ARCHITECTURE.md) — System architecture
- [`.ai/TESTING.md`](.ai/TESTING.md) — Test baseline and test file map
- [`.ai/AGENTS.md`](.ai/AGENTS.md) — Mirror of agent guardrails
- [`.ai/KNOWN_FAILURES.md`](.ai/KNOWN_FAILURES.md) — Recurring failure patterns

### For General Users

- [`docs/README.md`](docs/README.md) — Entry point for general documentation
- [`docs/system-context.md`](docs/system-context.md) — Global principles and constraints
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — System architecture and data flow
- [`docs/DECISIONS.md`](docs/DECISIONS.md) — Architecture decisions
- [`docs/PROJECT_MEMORY.md`](docs/PROJECT_MEMORY.md) — Project memory
- [`docs/ROADMAP.md`](docs/ROADMAP.md) — Implementation priorities
- [`docs/testing.md`](docs/testing.md) — Testing guide
- [`docs/TROUBLESHOOTING.md`](docs/TROUBLESHOOTING.md) — Troubleshooting
- [`docs/SPOTIFY_CALL_SURFACE.md`](docs/SPOTIFY_CALL_SURFACE.md) — Spotify compliance reference
- [`docs/SMOKE_TEST_NOTES.md`](docs/SMOKE_TEST_NOTES.md) — Manual smoke test checklist
- [`docs/source-material/README.md`](docs/source-material/README.md) — Archived historical context

### Cross-Reference

See [`.ai/README.md`](.ai/README.md) and [`docs/README.md`](docs/README.md) for full cross-reference maps between documentation directories.

## Setup

PowerShell:

```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -e ".[dev]"
Copy-Item .env.example .env
```

Bash/zsh:

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env
```

For Tidal playlist input, install the optional Tidal extra:

```bash
pip install -e ".[dev,tidal]"
```

Fill in `.env` as needed:

```bash
SPOTIPY_CLIENT_ID="your_spotify_client_id"
SPOTIPY_CLIENT_SECRET="your_spotify_client_secret"
SPOTIPY_REDIRECT_URI="http://127.0.0.1:8888/callback"
LASTFM_API_KEY="your_lastfm_api_key"
MB_CONTACT_EMAIL="you@example.com"
# Optional override. If unset, Mamonia uses a platform default:
# Windows: %LOCALAPPDATA%\Mamonia
# macOS:   ~/Library/Application Support/Mamonia
# Linux:   ${XDG_STATE_HOME}/mamonia or ~/.local/state/mamonia
MAMONIA_HOME="~/.local/state/mamonia"

TIDAL_CLIENT_ID=""
TIDAL_CLIENT_SECRET=""

LISTENBRAINZ_USER_TOKEN=""
```

Spotify credentials are required for Spotify playlist input and Spotify playlist creation. For local CLI PKCE, configure the exact `http://127.0.0.1:8888/callback` redirect URI from `.env` in the Spotify developer dashboard; non-loopback redirect URIs must use HTTPS, and `localhost` is intentionally rejected. Last.fm is optional but strongly recommended. ListenBrainz is optional and provides listener affinity data.

MusicBrainz contact email should be a real contact value, but Mamonia only warns if it is still the placeholder.

## Running

Open the guided UI:

```bash
mamonia
```

Check configuration without printing secrets:

```bash
mamonia diagnose
mamonia diagnose --playlist "https://open.spotify.com/playlist/PLAYLIST_ID"
```

Run discovery from a Spotify/Tidal/text/CSV source:

```bash
mamonia discover --playlist "https://open.spotify.com/playlist/PLAYLIST_ID"
mamonia discover --playlist "tracks.txt" --output csv
mamonia discover --playlist "My Spotify Library.csv" --global-memory
```

When a playlist matches a saved profile but has changed, Mamonia compares track identity rather than order. Shuffled playlists are treated as unchanged. If tracks were added or removed, Mamonia asks whether to update the connected taste profile and only enriches newly introduced artists.

Run discovery from an existing saved profile:

```bash
mamonia discover --profile 1
mamonia discover --profile spotify-PLAYLIST_ID
```

Preview safely:

```bash
mamonia discover --playlist "tracks.txt" --dry-run
```

Tune expensive runs:

```bash
mamonia discover --playlist "tracks.txt" --months 12 --max-tracks 3 --limit 100 --max-candidates 150
mamonia discover --playlist "tracks.txt" --discovery-mode deep_diverse
mamonia discover --playlist "tracks.txt" --force-refresh
mamonia discover --playlist "tracks.txt" --penalize-frequent-artists
mamonia discover --playlist "tracks.txt" --shuffle-releases
```

Create a private Spotify playlist too:

```bash
mamonia discover --playlist "tracks.txt" --create-spotify-playlist --spotify-playlist-name "Mamonia Fresh Finds"
```

## Inputs And Outputs

Text input supports:

```text
Artist - Track
Artist | Track | Album
```

CSV input supports TuneMyMusic-style columns:

- `Track name`
- `Artist name`
- `Album`
- `Playlist name`
- `Type`
- `ISRC`
- `Spotify - id`

CSV recommendation output uses the same importer-friendly shape, now with trailing diagnostics: `Track name`, `Artist name`, `Album`, `Playlist name`, `Type`, `ISRC`, `Spotify - id`, `Spotify URI`, `Spotify URL`, `Resolution source`, `Resolution note`, and `Spotify Attribution`. Album names are included so downstream importers have more matching context than plain artist/title text.

JSON export includes the same track-level diagnostics plus Spotify open URLs, an `attribution` block, and an `explain` block for each recommendation with candidate sources, provider evidence, filter/fallback context, and whether the final tracklist was Spotify-derived, MusicBrainz-derived, mixed, or unresolved.

Default export filenames include output type, a source hint, timestamp, and collision suffixes when needed, for example:

```text
my-spotify-l_23_04_26.csv
```

## Local Data

Mamonia stores local state in a stable per-user directory by default:

- Windows: `%LOCALAPPDATA%\Mamonia`
- macOS: `~/Library/Application Support/Mamonia`
- Linux: `${XDG_STATE_HOME}/mamonia` (or `~/.local/state/mamonia` when `XDG_STATE_HOME` is unset)

If those defaults cannot be resolved safely, Mamonia falls back to `~/.mamonia`.

- `mamonia.sqlite`
- reusable taste profiles
- seed-track exclusion keys
- cached source playlist state for delta updates
- heard releases
- provider API cache rows
- recommendation run metadata
- Spotify OAuth token cache

Set `MAMONIA_HOME` to override this directory explicitly (for example, project-local `./.mamonia`).

Heard matching is release-level and uses this precedence:

1. MusicBrainz release-group ID
2. Spotify album ID
3. normalized artist + normalized release title + release year

Input playlist releases are automatically marked as heard before discovery begins; exported recommendations are marked as heard after successful export. See [Heard Semantics](docs/ARCHITECTURE.md#heard-semantics) for details.

## Management Commands

```bash
mamonia heard list
mamonia heard add --artist "Artist" --release "Release" --release-date "2026-01-15"
mamonia heard remove --id 1
mamonia heard export-json --path heard_backup.json
mamonia heard import-json --path heard_backup.json
mamonia cache clear
mamonia profile list
mamonia profile show 1
mamonia profile refresh --playlist tracks.txt
mamonia profile delete 1
mamonia profile export --profile 1 --path profile.json
```

## Development

```bash
python -m pytest
python -m unittest discover tests
```

**Current Test Status (2026-04-30):** `python -m pytest -q` -> **6 failed, 688 passed, 1 skipped, 1 warning**.

**Last Known Green Baseline:** **661 passed, 1 skipped, 1 warning**.

See [`docs/testing.md`](docs/testing.md) for canonical test status and [`.ai/TESTING.md`](.ai/TESTING.md) for the mirror.

## Known Limitations

- MusicBrainz metadata is uneven for very new, obscure, regional, or messy releases.
- Native Tidal playlist creation is implemented but still marked experimental; verify with `mamonia tidal-smoke` before relying on it for routine workflows.
- Spotify playlist creation requires resolved Spotify track URIs; a local CSV backup is always written alongside the playlist. When URIs are absent, CSV export is the primary output.
- Remaining-time estimates are candidate-based; provider fan-out can make them approximate.
- The dated real-terminal smoke pass is still open documentation/product-polish work.
- Guided localization is implemented, but one real-terminal copy review pass is still worth doing.


