mamonia 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. mamonia-0.1.0/LICENSE +21 -0
  2. mamonia-0.1.0/PKG-INFO +381 -0
  3. mamonia-0.1.0/README.md +342 -0
  4. mamonia-0.1.0/mamonia/__init__.py +22 -0
  5. mamonia-0.1.0/mamonia/__main__.py +5 -0
  6. mamonia-0.1.0/mamonia/analysis.py +483 -0
  7. mamonia-0.1.0/mamonia/canonicalization.py +297 -0
  8. mamonia-0.1.0/mamonia/cli.py +751 -0
  9. mamonia-0.1.0/mamonia/config.py +323 -0
  10. mamonia-0.1.0/mamonia/deduplication.py +667 -0
  11. mamonia-0.1.0/mamonia/delta.py +54 -0
  12. mamonia-0.1.0/mamonia/discovery.py +697 -0
  13. mamonia-0.1.0/mamonia/discovery_window.py +91 -0
  14. mamonia-0.1.0/mamonia/enrichment.py +698 -0
  15. mamonia-0.1.0/mamonia/explanation.py +154 -0
  16. mamonia-0.1.0/mamonia/filtering/__init__.py +4 -0
  17. mamonia-0.1.0/mamonia/filtering/community_artists.py +383 -0
  18. mamonia-0.1.0/mamonia/filtering/profile_index.py +76 -0
  19. mamonia-0.1.0/mamonia/graph_expansion.py +709 -0
  20. mamonia-0.1.0/mamonia/i18n.py +196 -0
  21. mamonia-0.1.0/mamonia/input.py +209 -0
  22. mamonia-0.1.0/mamonia/interactive.py +1900 -0
  23. mamonia-0.1.0/mamonia/isrc.py +447 -0
  24. mamonia-0.1.0/mamonia/liquid_pulse.py +139 -0
  25. mamonia-0.1.0/mamonia/locales/en.json +231 -0
  26. mamonia-0.1.0/mamonia/locales/pl.json +231 -0
  27. mamonia-0.1.0/mamonia/matching.py +131 -0
  28. mamonia-0.1.0/mamonia/models.py +368 -0
  29. mamonia-0.1.0/mamonia/output.py +444 -0
  30. mamonia-0.1.0/mamonia/pipeline_v2.py +990 -0
  31. mamonia-0.1.0/mamonia/profiles.py +87 -0
  32. mamonia-0.1.0/mamonia/progress.py +564 -0
  33. mamonia-0.1.0/mamonia/prompts.py +15 -0
  34. mamonia-0.1.0/mamonia/providers/__init__.py +284 -0
  35. mamonia-0.1.0/mamonia/providers/deezer.py +237 -0
  36. mamonia-0.1.0/mamonia/providers/lastfm.py +165 -0
  37. mamonia-0.1.0/mamonia/providers/listenbrainz.py +425 -0
  38. mamonia-0.1.0/mamonia/providers/musicbrainz.py +618 -0
  39. mamonia-0.1.0/mamonia/providers/spotify.py +2044 -0
  40. mamonia-0.1.0/mamonia/providers/spotify_bkup.py +1352 -0
  41. mamonia-0.1.0/mamonia/providers/tidal.py +1322 -0
  42. mamonia-0.1.0/mamonia/release_discovery.py +413 -0
  43. mamonia-0.1.0/mamonia/scoring_v2.py +260 -0
  44. mamonia-0.1.0/mamonia/seed_weights.py +120 -0
  45. mamonia-0.1.0/mamonia/storage.py +1466 -0
  46. mamonia-0.1.0/mamonia/utils.py +401 -0
  47. mamonia-0.1.0/mamonia/workflow.py +2059 -0
  48. mamonia-0.1.0/mamonia.egg-info/PKG-INFO +381 -0
  49. mamonia-0.1.0/mamonia.egg-info/SOURCES.txt +83 -0
  50. mamonia-0.1.0/mamonia.egg-info/dependency_links.txt +1 -0
  51. mamonia-0.1.0/mamonia.egg-info/entry_points.txt +2 -0
  52. mamonia-0.1.0/mamonia.egg-info/requires.txt +21 -0
  53. mamonia-0.1.0/mamonia.egg-info/top_level.txt +1 -0
  54. mamonia-0.1.0/pyproject.toml +67 -0
  55. mamonia-0.1.0/setup.cfg +4 -0
  56. mamonia-0.1.0/tests/test_analysis.py +175 -0
  57. mamonia-0.1.0/tests/test_canonicalization.py +287 -0
  58. mamonia-0.1.0/tests/test_cli.py +1730 -0
  59. mamonia-0.1.0/tests/test_community_artists_filter.py +431 -0
  60. mamonia-0.1.0/tests/test_config.py +358 -0
  61. mamonia-0.1.0/tests/test_delta.py +79 -0
  62. mamonia-0.1.0/tests/test_discovery.py +816 -0
  63. mamonia-0.1.0/tests/test_discovery_window.py +24 -0
  64. mamonia-0.1.0/tests/test_docs_consistency.py +76 -0
  65. mamonia-0.1.0/tests/test_enrichment.py +636 -0
  66. mamonia-0.1.0/tests/test_explanation.py +163 -0
  67. mamonia-0.1.0/tests/test_graph_expansion.py +355 -0
  68. mamonia-0.1.0/tests/test_i18n.py +141 -0
  69. mamonia-0.1.0/tests/test_input.py +196 -0
  70. mamonia-0.1.0/tests/test_isrc.py +388 -0
  71. mamonia-0.1.0/tests/test_matching.py +120 -0
  72. mamonia-0.1.0/tests/test_musicbrainz.py +116 -0
  73. mamonia-0.1.0/tests/test_output.py +373 -0
  74. mamonia-0.1.0/tests/test_pipeline_v2.py +674 -0
  75. mamonia-0.1.0/tests/test_profiles.py +102 -0
  76. mamonia-0.1.0/tests/test_progress.py +206 -0
  77. mamonia-0.1.0/tests/test_providers.py +1274 -0
  78. mamonia-0.1.0/tests/test_release_discovery.py +407 -0
  79. mamonia-0.1.0/tests/test_scoring_v2.py +299 -0
  80. mamonia-0.1.0/tests/test_seed_weights.py +135 -0
  81. mamonia-0.1.0/tests/test_spotify.py +1571 -0
  82. mamonia-0.1.0/tests/test_storage.py +537 -0
  83. mamonia-0.1.0/tests/test_track_deduplication.py +80 -0
  84. mamonia-0.1.0/tests/test_variant_handling.py +133 -0
  85. mamonia-0.1.0/tests/test_workflow.py +894 -0
mamonia-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kajetan Jagiełka
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
mamonia-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,381 @@
1
+ Metadata-Version: 2.4
2
+ Name: mamonia
3
+ Version: 0.1.0
4
+ Summary: Local-first music discovery CLI inspired by Prawo Mamonia.
5
+ Author-email: Kajetan Jagiełka <heartless-jeden@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://heartless.one/
8
+ Project-URL: Repository, https://github.com/heartless-jeden/mamonia
9
+ Project-URL: Issues, https://github.com/heartless-jeden/mamonia/issues
10
+ Keywords: music,discovery,spotify,cli,musicbrainz,lastfm
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: Topic :: Multimedia :: Sound/Audio
17
+ Requires-Python: >=3.12
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: typer>=0.12
21
+ Requires-Dist: pydantic>=2.7
22
+ Requires-Dist: httpx>=0.27
23
+ Requires-Dist: spotipy>=2.23
24
+ Requires-Dist: python-dotenv>=1.0
25
+ Requires-Dist: questionary>=2.0
26
+ Requires-Dist: rapidfuzz>=3.0
27
+ Requires-Dist: rich>=13.0
28
+ Requires-Dist: numpy>=1.26
29
+ Requires-Dist: pandas>=2.2
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=8.0; extra == "dev"
32
+ Requires-Dist: pytest-xdist>=3.0; extra == "dev"
33
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
34
+ Provides-Extra: tidal
35
+ Requires-Dist: tidalapi>=0.8.0; extra == "tidal"
36
+ Provides-Extra: csvtool
37
+ Requires-Dist: questionary>=2.0; extra == "csvtool"
38
+ Dynamic: license-file
39
+
40
+ # Mamonia
41
+
42
+ A music discovery CLI tool with an intuitive interface that keeps it local.
43
+ Inspired by Engineer Mamoń's Principle / Prawo inżyniera Mamonia.
44
+
45
+ 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.
46
+ It remembers what it's already recommended so you don't get the same suggestions twice. Never ever.
47
+
48
+ 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.
49
+ Spotify's just there acting as a catalog and to help fill in metadata gaps.
50
+
51
+ ## Current Features
52
+
53
+ - Guided interactive menu: run `mamonia --help` to see all commands, or run bare `mamonia` for the guided interactive menu.
54
+ - Guided localization with English and Polish locale files, plus a saved language preference for the menu flow.
55
+ - Guided profile reuse decisions based on real playlist delta and enrichment completeness.
56
+ - Durable SQLite artist/track enrichment reused across runs and saved profiles.
57
+ - Guided library maintenance for heard releases, cache management, saved profile backup/export/import, and Spotify enrichment refresh.
58
+ - Supported inputs:
59
+ - preferred: Spotify playlist URL / ID or TuneMyMusic-style `.csv`,
60
+ - also viable: Tidal playlist URL/ID or a simple `.txt`.
61
+ - Outputs: TuneMyMusic-style `.csv`, plain `.txt`, and JSON.
62
+ - 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.
63
+ - Reusable source profiles keyed by playlist ID or order-independent text/CSV track-set hash.
64
+ - Delta updates for changed playlists: Mamonia detects additions/removals, asks before updating, and reuses existing data.
65
+ - Global memory mode to exclude known tracks from every saved profile, not only the active source.
66
+ - Input playlist releases are automatically marked as heard before discovery begins, preventing recommendations from the user's existing collection.
67
+ - Permanent release-level heard list; successful exports automatically mark releases as heard.
68
+ - Mandatory discovery progress with one updating Rich/ASCII block in interactive terminals and compact fallback lines in redirected output.
69
+ - Dry-run previews that do not write exports, mark heard releases, or create playlists.
70
+ - Provider-aware rate-limit warnings and `mamonia diagnose` for local state, cache stats, provider TTLs, and credential checks.
71
+ - SQLite API cache for provider data, including normalized cache keys, provider-specific TTLs, `--force-refresh`, Spotify artist/album/tracklist lookups, and negative Spotify misses.
72
+ - Variant-aware duplicate and heard matching for deluxe/remaster/regional/anniversary-style releases when stable IDs are missing.
73
+ - Cache-first Spotify track resolution with MusicBrainz tracklist fallback, Spotify track backfill, and conservative representative-track fallback.
74
+ - 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.
75
+ - Discovery scoring improvements including serendipity weighting, `--discovery-mode`, and label-affinity candidates from MusicBrainz.
76
+ - JSON explainability fields that summarize candidate source, provider evidence, filter/fallback context, and tracklist derivation.
77
+ - CSV/JSON track diagnostics including `Spotify URI`, `Spotify URL`, `Spotify Attribution`, `Resolution source`, and `Resolution note`.
78
+ - Tidal feasibility diagnostics in `mamonia diagnose`, plus experimental native Tidal playlist creation (`mamonia tidal-playlists`, `mamonia tidal-smoke`).
79
+
80
+ ### Human Logic Proposal Pipeline
81
+
82
+ - Tier-driven discovery (Tier 1–4): seed artists → MusicBrainz collaborators → ListenBrainz similarity → Last.fm similarity.
83
+ - Structured recommendation explanations (seed connections, confidence, matching tags).
84
+ - Balanced v2 selection defaults to a high/medium/low confidence mix with diversity pressure; `top_first` remains available for strict score ordering.
85
+ - Optional v2 controls can penalize artists with many recent EPs/singles and deterministically shuffle whole release recommendations.
86
+ - Scoring with multi-seed diminishing returns (100%/70%/40%/20%) and modifiers for reissues, uncertainty, and tag-only matches.
87
+ - Track enrichment pipeline (Spotify → MusicBrainz → ISRC fallback via Tidal/Deezer).
88
+ - Enrichment phase separation:
89
+ - discovery-time candidate browsing uses lightweight enrichment only (MusicBrainz + ISRC fallback),
90
+ - full enrichment (including Spotify lookup/backfill/recovery) runs only after final release candidates are selected.
91
+ - 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.
92
+
93
+ Mamonia deliberately does not use Spotify Audio Features, Audio Analysis, Recommendations, or Related Artists.
94
+
95
+ ## How It Works: The Simple Version
96
+
97
+ **Building your taste profile:**
98
+
99
+ 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:
100
+ - **Spotify** — what else are they famous for
101
+ - **MusicBrainz** — who do they collaborate with, which bands are related
102
+ - **Last.fm** — what tags do listeners use, who else listens to them
103
+ - **ListenBrainz** — how many listeners share taste (which ones also listen to other artists)
104
+
105
+ 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."
106
+
107
+ **Finding recommendations (default mode):**
108
+
109
+ When you ask for recommendations, Mamonia:
110
+
111
+ 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)."
112
+
113
+ 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.
114
+
115
+ 3. **Filters** — removes old stuff you asked for (e.g., last 6 months), removes albums already in your heard list, removes bad matches.
116
+
117
+ 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.
118
+
119
+ 5. **Outputs** — gives you the top results as a CSV/JSON/TXT file, with links to Spotify (if the tracks were found there).
120
+
121
+ **Concrete example:**
122
+
123
+ You have a Spotify playlist: `Massive Attack`, `Portishead`, `Burial`.
124
+
125
+ Mamonia learns:
126
+ - Massive Attack worked with **Ghostpoet** and is related to **Portishead**
127
+ - Portishead is similar to **Burial**
128
+ - All three are tagged "trip-hop" or "electronic" in Last.fm
129
+
130
+ When you ask for recommendations, Mamonia finds:
131
+ - A new Ghostpoet album (Massive Attack collaborator) → scores high
132
+ - A new album by an artist Portishead's listeners also love → scores medium
133
+ - 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.
134
+
135
+ Result: you get the Ghostpoet album first, and the high-confidence connections, not a random artist filling space.
136
+
137
+ **What makes it different from Spotify:**
138
+
139
+ - Spotify's algorithm is a black box. Mamonia shows you **why** it picked something.
140
+ - Spotify's algorithm may prioritize what makes them money. Mamonia just walks the graph of actual artist connections.
141
+ - You keep **full control** — all data lives on your machine. No tracking, no streaming history leaks, no ads.
142
+ - Results are **reproducible** — same input, same profile, same date range = same output. No surprises due to A/B tests.
143
+
144
+ ## Documentation
145
+
146
+ ### Unified Documentation Structure
147
+
148
+ This project maintains two documentation directories with a clear **hierarchy of truth**:
149
+
150
+ 1. **Source Code** — The most up-to-date reference; code is canonical
151
+ 2. **`docs/` Documentation** — Canonical documentation for all models and humans
152
+ 3. **`.ai/` Documentation** — Active compatibility mirror of canonical docs
153
+
154
+ ### For AI/Automation Mirrors
155
+
156
+ - [`.ai/README.md`](.ai/README.md) — Entry point for Claude documentation
157
+ - [`.ai/MEMORY.md`](.ai/MEMORY.md) — Current project state and constraints
158
+ - [`.ai/ARCHITECTURE.md`](.ai/ARCHITECTURE.md) — System architecture
159
+ - [`.ai/TESTING.md`](.ai/TESTING.md) — Test baseline and test file map
160
+ - [`.ai/AGENTS.md`](.ai/AGENTS.md) — Mirror of agent guardrails
161
+ - [`.ai/KNOWN_FAILURES.md`](.ai/KNOWN_FAILURES.md) — Recurring failure patterns
162
+
163
+ ### For General Users
164
+
165
+ - [`docs/README.md`](docs/README.md) — Entry point for general documentation
166
+ - [`docs/system-context.md`](docs/system-context.md) — Global principles and constraints
167
+ - [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — System architecture and data flow
168
+ - [`docs/DECISIONS.md`](docs/DECISIONS.md) — Architecture decisions
169
+ - [`docs/PROJECT_MEMORY.md`](docs/PROJECT_MEMORY.md) — Project memory
170
+ - [`docs/ROADMAP.md`](docs/ROADMAP.md) — Implementation priorities
171
+ - [`docs/testing.md`](docs/testing.md) — Testing guide
172
+ - [`docs/TROUBLESHOOTING.md`](docs/TROUBLESHOOTING.md) — Troubleshooting
173
+ - [`docs/SPOTIFY_CALL_SURFACE.md`](docs/SPOTIFY_CALL_SURFACE.md) — Spotify compliance reference
174
+ - [`docs/SMOKE_TEST_NOTES.md`](docs/SMOKE_TEST_NOTES.md) — Manual smoke test checklist
175
+ - [`docs/source-material/README.md`](docs/source-material/README.md) — Archived historical context
176
+
177
+ ### Cross-Reference
178
+
179
+ See [`.ai/README.md`](.ai/README.md) and [`docs/README.md`](docs/README.md) for full cross-reference maps between documentation directories.
180
+
181
+ ## Setup
182
+
183
+ PowerShell:
184
+
185
+ ```powershell
186
+ python -m venv .venv
187
+ .\.venv\Scripts\Activate.ps1
188
+ pip install -e ".[dev]"
189
+ Copy-Item .env.example .env
190
+ ```
191
+
192
+ Bash/zsh:
193
+
194
+ ```bash
195
+ python3 -m venv .venv
196
+ source .venv/bin/activate
197
+ pip install -e ".[dev]"
198
+ cp .env.example .env
199
+ ```
200
+
201
+ For Tidal playlist input, install the optional Tidal extra:
202
+
203
+ ```bash
204
+ pip install -e ".[dev,tidal]"
205
+ ```
206
+
207
+ Fill in `.env` as needed:
208
+
209
+ ```bash
210
+ SPOTIPY_CLIENT_ID="your_spotify_client_id"
211
+ SPOTIPY_CLIENT_SECRET="your_spotify_client_secret"
212
+ SPOTIPY_REDIRECT_URI="http://127.0.0.1:8888/callback"
213
+ LASTFM_API_KEY="your_lastfm_api_key"
214
+ MB_CONTACT_EMAIL="you@example.com"
215
+ # Optional override. If unset, Mamonia uses a platform default:
216
+ # Windows: %LOCALAPPDATA%\Mamonia
217
+ # macOS: ~/Library/Application Support/Mamonia
218
+ # Linux: ${XDG_STATE_HOME}/mamonia or ~/.local/state/mamonia
219
+ MAMONIA_HOME="~/.local/state/mamonia"
220
+
221
+ TIDAL_CLIENT_ID=""
222
+ TIDAL_CLIENT_SECRET=""
223
+
224
+ LISTENBRAINZ_USER_TOKEN=""
225
+ ```
226
+
227
+ 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.
228
+
229
+ MusicBrainz contact email should be a real contact value, but Mamonia only warns if it is still the placeholder.
230
+
231
+ ## Running
232
+
233
+ Open the guided UI:
234
+
235
+ ```bash
236
+ mamonia
237
+ ```
238
+
239
+ Check configuration without printing secrets:
240
+
241
+ ```bash
242
+ mamonia diagnose
243
+ mamonia diagnose --playlist "https://open.spotify.com/playlist/PLAYLIST_ID"
244
+ ```
245
+
246
+ Run discovery from a Spotify/Tidal/text/CSV source:
247
+
248
+ ```bash
249
+ mamonia discover --playlist "https://open.spotify.com/playlist/PLAYLIST_ID"
250
+ mamonia discover --playlist "tracks.txt" --output csv
251
+ mamonia discover --playlist "My Spotify Library.csv" --global-memory
252
+ ```
253
+
254
+ 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.
255
+
256
+ Run discovery from an existing saved profile:
257
+
258
+ ```bash
259
+ mamonia discover --profile 1
260
+ mamonia discover --profile spotify-PLAYLIST_ID
261
+ ```
262
+
263
+ Preview safely:
264
+
265
+ ```bash
266
+ mamonia discover --playlist "tracks.txt" --dry-run
267
+ ```
268
+
269
+ Tune expensive runs:
270
+
271
+ ```bash
272
+ mamonia discover --playlist "tracks.txt" --months 12 --max-tracks 3 --limit 100 --max-candidates 150
273
+ mamonia discover --playlist "tracks.txt" --discovery-mode deep_diverse
274
+ mamonia discover --playlist "tracks.txt" --force-refresh
275
+ mamonia discover --playlist "tracks.txt" --penalize-frequent-artists
276
+ mamonia discover --playlist "tracks.txt" --shuffle-releases
277
+ ```
278
+
279
+ Create a private Spotify playlist too:
280
+
281
+ ```bash
282
+ mamonia discover --playlist "tracks.txt" --create-spotify-playlist --spotify-playlist-name "Mamonia Fresh Finds"
283
+ ```
284
+
285
+ ## Inputs And Outputs
286
+
287
+ Text input supports:
288
+
289
+ ```text
290
+ Artist - Track
291
+ Artist | Track | Album
292
+ ```
293
+
294
+ CSV input supports TuneMyMusic-style columns:
295
+
296
+ - `Track name`
297
+ - `Artist name`
298
+ - `Album`
299
+ - `Playlist name`
300
+ - `Type`
301
+ - `ISRC`
302
+ - `Spotify - id`
303
+
304
+ 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.
305
+
306
+ 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.
307
+
308
+ Default export filenames include output type, a source hint, timestamp, and collision suffixes when needed, for example:
309
+
310
+ ```text
311
+ my-spotify-l_23_04_26.csv
312
+ ```
313
+
314
+ ## Local Data
315
+
316
+ Mamonia stores local state in a stable per-user directory by default:
317
+
318
+ - Windows: `%LOCALAPPDATA%\Mamonia`
319
+ - macOS: `~/Library/Application Support/Mamonia`
320
+ - Linux: `${XDG_STATE_HOME}/mamonia` (or `~/.local/state/mamonia` when `XDG_STATE_HOME` is unset)
321
+
322
+ If those defaults cannot be resolved safely, Mamonia falls back to `~/.mamonia`.
323
+
324
+ - `mamonia.sqlite`
325
+ - reusable taste profiles
326
+ - seed-track exclusion keys
327
+ - cached source playlist state for delta updates
328
+ - heard releases
329
+ - provider API cache rows
330
+ - recommendation run metadata
331
+ - Spotify OAuth token cache
332
+
333
+ Set `MAMONIA_HOME` to override this directory explicitly (for example, project-local `./.mamonia`).
334
+
335
+ Heard matching is release-level and uses this precedence:
336
+
337
+ 1. MusicBrainz release-group ID
338
+ 2. Spotify album ID
339
+ 3. normalized artist + normalized release title + release year
340
+
341
+ 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.
342
+
343
+ ## Management Commands
344
+
345
+ ```bash
346
+ mamonia heard list
347
+ mamonia heard add --artist "Artist" --release "Release" --release-date "2026-01-15"
348
+ mamonia heard remove --id 1
349
+ mamonia heard export-json --path heard_backup.json
350
+ mamonia heard import-json --path heard_backup.json
351
+ mamonia cache clear
352
+ mamonia profile list
353
+ mamonia profile show 1
354
+ mamonia profile refresh --playlist tracks.txt
355
+ mamonia profile delete 1
356
+ mamonia profile export --profile 1 --path profile.json
357
+ ```
358
+
359
+ ## Development
360
+
361
+ ```bash
362
+ python -m pytest
363
+ python -m unittest discover tests
364
+ ```
365
+
366
+ **Current Test Status (2026-04-30):** `python -m pytest -q` -> **6 failed, 688 passed, 1 skipped, 1 warning**.
367
+
368
+ **Last Known Green Baseline:** **661 passed, 1 skipped, 1 warning**.
369
+
370
+ See [`docs/testing.md`](docs/testing.md) for canonical test status and [`.ai/TESTING.md`](.ai/TESTING.md) for the mirror.
371
+
372
+ ## Known Limitations
373
+
374
+ - MusicBrainz metadata is uneven for very new, obscure, regional, or messy releases.
375
+ - Native Tidal playlist creation is implemented but still marked experimental; verify with `mamonia tidal-smoke` before relying on it for routine workflows.
376
+ - 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.
377
+ - Remaining-time estimates are candidate-based; provider fan-out can make them approximate.
378
+ - The dated real-terminal smoke pass is still open documentation/product-polish work.
379
+ - Guided localization is implemented, but one real-terminal copy review pass is still worth doing.
380
+
381
+