🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

Ultimate Music Manager

by @drajb

Organises a messy local music library into a clean Language/Artist/Album hierarchy using acoustic fingerprinting, deduplication, metadata enrichment, and opt...

TERMINAL
clawhub install ultimate-music-manager

πŸ“– About This Skill


name: ultimate-music-manager description: "Organises a messy local music library into a clean Language/Artist/Album hierarchy using acoustic fingerprinting, deduplication, metadata enrichment, and optional Spotify sync. Use when: (1) User wants to sort or clean up a folder of audio files, (2) User has untagged or badly-tagged MP3/FLAC/M4A files, (3) User wants to identify unknown songs via Shazam fingerprinting, (4) User wants to deduplicate audio files by content hash, (5) User wants to enrich metadata with iTunes artwork and LrcLib lyrics, (6) User wants to sync their local library to Spotify playlists." metadata: openclaw: requires: env: - MUSIC_ROOT optionalEnv: - SPOTIFY_CLIENT_ID - SPOTIFY_CLIENT_SECRET - SPOTIFY_REDIRECT_URI - SHAZAM_CONCURRENCY - ITUNES_COUNTRIES - SORTED_ROOT - DATA_DIR - FFMPEG_BIN bins: - python3.12 - git optionalBins: - ffmpeg primaryEnv: MUSIC_ROOT source: https://github.com/drajb/sonic-phoenix

Sonic Phoenix

A multi-phase pipeline that transforms a disorganised local music collection into a pristine, fully-tagged library sorted by /// - .<ext></code>. Works with any language β€” Hindi, English, Japanese, Spanish, or anything else your collection contains.</p><p style="margin:8px 0">The pipeline never deletes audio files. Suspected duplicates are moved to a staging folder for manual review.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Quick Reference</h3></p><p style="margin:8px 0">| Situation | Action | |-----------|--------| | First-time setup | <a href="#environment-setup" target="_blank" rel="noopener" style="color:#6366f1">Environment Setup</a> | | Sort a messy music folder | Run Phases 1-5 in order | | Identify unknown songs | Run Phase 1 (Shazam fingerprinting) | | Find and remove duplicates | Run Phase 2 (SHA-256 catalog) | | Fix bad/missing ID3 tags | Run Phase 4 (iTunes enrichment) | | Sync library to Spotify | Run Phase 6 (requires Spotify app credentials) | | Add language classification hints | Create hint files in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">config/language_hints/</code> | | Check current configuration | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">python config.py</code> |</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Environment Setup</h3></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Prerequisites</h4></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Python 3.12</strong> (required β€” <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">shazamio-core</code> only ships wheels for 3.10-3.12)</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Git</strong> (to clone the repo)</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">FFmpeg</strong> (optional β€” only needed if your collection contains non-MP3 formats like FLAC, OGG, or WMA that Shazam needs to decode)</li></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Installation</h4></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"># Clone into your music folder (recommended) or anywhere else cd /path/to/your/music git clone https://github.com/drajb/sonic-phoenix.git cd sonic-phoenix</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Create virtual environment with Python 3.12</h2> python3.12 -m venv .venv</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Activate</h2> source .venv/bin/activate # macOS / Linux <h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">.venv\Scripts\activate # Windows PowerShell</h2> <h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">.venv\Scripts\activate.bat # Windows cmd</h2></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Install dependencies</h2> pip install -r requirements.txt </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Configuration</h4></p><p style="margin:8px 0">Create a <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code> file in the repo root (copy from <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env.example</code>):</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"># REQUIRED β€” absolute path to the root of your music collection MUSIC_ROOT=/path/to/your/music</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">OPTIONAL β€” override any of these defaults:</h2> <h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">SORTED_ROOT=/path/to/your/music/Sorted</h2> <h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">DATA_DIR=/path/to/your/music/.data</h2> <h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">SHAZAM_CONCURRENCY=20</h2> <h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">ITUNES_COUNTRIES=US,GB</h2> </code></pre></p><p style="margin:8px 0">Verify configuration:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">python config.py </code></pre></p><p style="margin:8px 0">This prints all resolved paths and confirms <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">MUSIC_ROOT</code> exists.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Directory Layout After Setup</h4></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"><MUSIC_ROOT>/ β”œβ”€β”€ sonic-phoenix/ # This repo β”‚ β”œβ”€β”€ config.py # Central configuration (single source of truth) β”‚ β”œβ”€β”€ .env # Your local overrides (gitignored) β”‚ β”œβ”€β”€ 01A_extract_metadata.py β”‚ β”œβ”€β”€ 01D_shazam_all_files.py β”‚ β”œβ”€β”€ ... β”‚ └── config/ β”‚ └── language_hints/ # Optional language classification overrides β”‚ └── examples/ # Templates to copy and customise β”œβ”€β”€ Sorted/ # Pipeline output (auto-created) β”‚ β”œβ”€β”€ English/ β”‚ β”‚ β”œβ”€β”€ Adele/ β”‚ β”‚ β”‚ └── 25/ β”‚ β”‚ β”‚ └── Adele - Hello.mp3 β”‚ β”‚ └── ... β”‚ β”œβ”€β”€ Hindi/ β”‚ └── <any language langdetect finds>/ β”œβ”€β”€ .data/ # Machine-generated JSON artifacts (auto-created) β”‚ β”œβ”€β”€ metadata_catalog.json β”‚ β”œβ”€β”€ shazam_final_results.json β”‚ β”œβ”€β”€ catalog.json β”‚ β”œβ”€β”€ final_catalog.json β”‚ └── ... └── <your unsorted audio files> </code></pre></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">The Pipeline</h3></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Happy Path (Minimum Viable Run)</h4></p><p style="margin:8px 0">For a quick, effective sort of your library, run these scripts in order:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">python 01A_extract_metadata.py # Extract ID3/FLAC tags python 01D_shazam_all_files.py # Acoustic fingerprint every file via Shazam python 02A_catalog_music.py # SHA-256 hash catalog + duplicate detection python 02D_organize_music.py # Sort into Language/Artist/Album hierarchy python 03A_consolidate_by_artist.py --dry-run # Preview artist folder merges python 03A_consolidate_by_artist.py # Execute merges python 03D_titanium_resort.py # Final structural enforcement pass python 04I_polish_and_enrich_v6.py # Enrich tags via iTunes + embed artwork python 05I_finalize_catalog.py # Lock the master catalog </code></pre></p><p style="margin:8px 0">Each script is resumable β€” if interrupted, re-run it and it picks up where it left off.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Phase 1: Extraction and Identification</h4></p><p style="margin:8px 0">| Script | Status | Purpose | |--------|--------|---------| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">01A_extract_metadata.py</code> | CANONICAL | Reads existing ID3/FLAC/MP4 tags via Mutagen. Splits files into <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">tagged_files</code> and <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">untagged_files</code> in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">metadata_catalog.json</code>. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">01B_shazam_identify.py</code> | UTILITY | Shazam-identify only the untagged files from 01A. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">01C_shazam_by_hash.py</code> | UTILITY | Propagate a single Shazam match to all byte-identical copies (via hash groups from Phase 2). | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">01D_shazam_all_files.py</code> | CANONICAL | Force every audio file through Shazam regardless of existing tags. Use this when tags are unreliable. 20-way concurrent by default. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">01E_test_matching.py</code> | UTILITY | Spot-check a handful of files against Shazam to verify the pipeline is working. |</p><p style="margin:8px 0"><strong style="color:#e5e7eb">When to use 01B vs 01D:</strong> If your collection has clean tags on most files, use 01A then 01B (faster β€” only fingerprints untagged files). If tags are unreliable (generic names like "Track 01", meaningless album fields, garbled metadata), use 01D to fingerprint everything and cross-validate against the acoustic results.</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Shazam rate limiting:</strong> If you see HTTP 429 errors, lower <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">SHAZAM_CONCURRENCY</code> in your <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code> (try 5-10).</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Phase 2: Cataloguing and Deduplication</h4></p><p style="margin:8px 0">| Script | Status | Purpose | |--------|--------|---------| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">02A_catalog_music.py</code> | CANONICAL | SHA-256 hash every audio file. Writes <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">catalog.json</code> with duplicate groups. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">02B_analyze_catalog.py</code> | UTILITY | Analyses the hash catalog and prints duplicate statistics. Read-only reporter. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">02C_organize_files.py</code> | UTILITY | Organises files using the hash catalog and final catalog. Alternative to 02D. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">02D_organize_music.py</code> | CANONICAL | The main sorter. Reads Shazam results and moves files into <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><SORTED_ROOT>/<Language>/<Artist>/<Album>/</code>. |</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Phase 3: Consolidation and Structural Cleanup</h4></p><p style="margin:8px 0">| Script | Status | Purpose | |--------|--------|---------| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">03A_consolidate_by_artist.py</code> | UTILITY | Merge fragmented artist folders (e.g. "Akon feat Eminem" into "Akon"). Supports <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--dry-run</code>. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">03D_titanium_resort.py</code> | CANONICAL | Final structural enforcer. Any file not matching <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">Language/Artist/Album/</code> gets re-sorted. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">03F_reorganize_binary.py</code> | UTILITY | Resolve near-duplicate files via binary comparison. |</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Phase 4: Metadata Enrichment</h4></p><p style="margin:8px 0">| Script | Status | Purpose | |--------|--------|---------| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">04I_polish_and_enrich_v6.py</code> | CANONICAL | The single enrichment entry point. Cleans junk from tags (quality markers, description artifacts, miscellaneous noise), enriches via iTunes Search API (artist, title, album, release date, artwork), fetches synchronized lyrics from LrcLib, embeds cover art into ID3 tags. |</p><p style="margin:8px 0"><strong style="color:#e5e7eb">iTunes rate limiting:</strong> The script rotates queries across country codes (default: US, GB). Add more via <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ITUNES_COUNTRIES=US,GB,AU,CA</code> in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code>.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Phase 5: Finalization</h4></p><p style="margin:8px 0">| Script | Status | Purpose | |--------|--------|---------| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05I_finalize_catalog.py</code> | CANONICAL | Merges ID3 tags, Shazam results, and NLP language classification into <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">final_catalog.json</code>. This is the read-only master snapshot of the library. |</p><p style="margin:8px 0">Supporting utilities: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05A</code> (JSON repair), <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05B</code> (sanitize results), <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05C</code> (confidence audit), <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05D</code> (residue purge), <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05E</code>/<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05H</code> (empty-folder vacuum).</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Phase 6: Spotify Sync (Optional)</h4></p><p style="margin:8px 0">Requires a free Spotify Developer account.</p><p style="margin:8px 0">| Script | Status | Purpose | |--------|--------|---------| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">06B_spotify_setup.py</code> | CANONICAL | OAuth handshake β€” run once to cache the token. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">06C_spotify_backup.py</code> | CANONICAL | Snapshot all existing playlists before making changes. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">06D_spotify_sync_engine.py</code> | CANONICAL | Creates per-language playlists mirroring your local library. Resumable. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">06E_spotify_discovery_sync.py</code> | CANONICAL | Cross-references local artists with Spotify listening history and generates genre-based "Essentials" playlists. |</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Spotify setup:</strong></p><p style="margin:8px 0">1. Create an app at https://developer.spotify.com/dashboard 2. Set Redirect URI to <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">http://127.0.0.1:8888/callback</code> 3. Add to <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code>: <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"> SPOTIFY_CLIENT_ID=your_client_id SPOTIFY_CLIENT_SECRET=your_client_secret </code></pre> 4. Run <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">python 06B_spotify_setup.py</code> β€” browser opens, approve scopes, token is cached.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Language Classification</h3></p><p style="margin:8px 0">The pipeline uses <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">langdetect</code> (NLP) to classify tracks by language automatically. No hardcoded language list β€” it creates whatever folders your collection needs.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Language Hints (Optional)</h4></p><p style="margin:8px 0">For artists that NLP can't classify from Latin-script titles (e.g. Bollywood artists romanised as "Arijit Singh"), create hint files:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">config/language_hints/ β”œβ”€β”€ examples/ # Copy and customise these templates β”‚ β”œβ”€β”€ English.json β”‚ β”œβ”€β”€ Hindi.json β”‚ └── README.md # Full schema reference β”œβ”€β”€ Hindi.json # Your custom hints (create from examples) └── Spanish.json </code></pre></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Hint file schema:</strong></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">{ "artists": ["Arijit Singh", "A.R. Rahman", "Shreya Ghoshal"], "dna": ["bollywood", "bhangra", "desi"], "keywords": ["ishq", "dil", "pyaar"], "lang_codes": ["hi", "pa"] } </code></pre></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">artists</code> β€” exact artist names to classify into this language</li> <li style="color:#94a3b8;margin:3px 0"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">dna</code> β€” substrings to match in folder/file paths</li> <li style="color:#94a3b8;margin:3px 0"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">keywords</code> β€” substrings to match in track titles</li> <li style="color:#94a3b8;margin:3px 0"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">lang_codes</code> β€” ISO 639-1 codes that <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">langdetect</code> might return for this language</li></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Configuration Reference</h3></p><p style="margin:8px 0">All configuration lives in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">config.py</code> and is driven by environment variables (set in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code> or your shell).</p><p style="margin:8px 0">| Variable | Required | Default | Purpose | |----------|----------|---------|---------| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">MUSIC_ROOT</code> | Yes | Parent of repo dir | Root of your music collection | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">SORTED_ROOT</code> | No | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><MUSIC_ROOT>/Sorted</code> | Destination for sorted files | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">DATA_DIR</code> | No | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><MUSIC_ROOT>/.data</code> | Machine-generated JSON artifacts | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">DUPLICATES_STAGING</code> | No | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><repo>/Duplicates_Staging</code> | Quarantine for suspected duplicates | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">UNIDENTIFIED_DIR</code> | No | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><SORTED_ROOT>/Unidentified</code> | Files Shazam couldn't identify | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">FFMPEG_BIN</code> | No | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><MUSIC_ROOT>/ffmpeg/bin</code> | Portable FFmpeg path (Windows) | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">SHAZAM_CONCURRENCY</code> | No | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">20</code> | Parallel Shazam calls (lower if rate-limited) | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ITUNES_COUNTRIES</code> | No | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">US,GB</code> | Country codes for iTunes API rotation | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">SPOTIFY_CLIENT_ID</code> | Phase 6 only | β€” | Spotify app client ID | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">SPOTIFY_CLIENT_SECRET</code> | Phase 6 only | β€” | Spotify app client secret | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">SPOTIFY_REDIRECT_URI</code> | No | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">http://127.0.0.1:8888/callback</code> | Spotify OAuth redirect |</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Supported Audio Formats</h3></p><p style="margin:8px 0"><code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.mp3</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.m4a</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.flac</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.wav</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.ogg</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.wma</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.aac</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.opus</code></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Safety Guarantees</h3></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">No deletion.</strong> Audio files are never deleted. Duplicates are moved to <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">Duplicates_Staging/</code> for manual review.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">No writes into the music hierarchy.</strong> All working state lives in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><DATA_DIR></code> or the repo directory.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Resumable.</strong> Every script can be interrupted and re-run safely.</li> <li style="color:#94a3b8;margin:3px 0"><strong style="color:#e5e7eb">Non-destructive tags.</strong> Enrichment writes new tags but does not remove existing valid data.</li></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Troubleshooting</h3></p><p style="margin:8px 0">| Problem | Fix | |---------|-----| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">shazamio-core</code> fails to install | You're on Python 3.13+. Use Python 3.12: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">python3.12 -m venv .venv</code> | | Shazam returns HTTP 429 | Lower <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">SHAZAM_CONCURRENCY</code> to 5-10 in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code> | | iTunes returns 403 | Add more country codes: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ITUNES_COUNTRIES=US,GB,AU,CA,DE</code> | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">MUSIC_ROOT does not exist</code> | Set <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">MUSIC_ROOT</code> in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code> to the absolute path of your music folder | | Tags still show junk after enrichment | Re-run <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">04I_polish_and_enrich_v6.py</code> β€” it's idempotent | | Spotify 403 on playlist creation | Add your Spotify account under "Users and Access" in the Developer Dashboard (Development Mode restriction) | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">langdetect</code> misclassifies a language | Create a hint file in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">config/language_hints/</code> for that language | | Empty artist/album folders after sorting | Run <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05E_final_cleanup.py</code> or <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05H_final_vacuum.py</code> to vacuum empties |</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Scripts</h3></p><p style="margin:8px 0">Helper scripts that automate common workflows. Run from the repo root.</p><p style="margin:8px 0">| Script | Purpose | | ------ | ------- | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ultimate-music-manager/scripts/preflight.sh</code> | Validates Python 3.12, venv, dependencies, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">MUSIC_ROOT</code>, FFmpeg. Run before first pipeline execution. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ultimate-music-manager/scripts/run-pipeline.sh</code> | Executes the happy-path sequence (Phases 1-5) with progress and timing. Supports <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--skip-shazam</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--spotify</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">--dry-run</code>. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ultimate-music-manager/scripts/status.sh</code> | Dashboard showing file counts, language breakdown, data file status, and pipeline progress. |</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Usage</h4></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"># Check environment is ready bash ultimate-music-manager/scripts/preflight.sh</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Preview what the pipeline will do</h2> bash ultimate-music-manager/scripts/run-pipeline.sh --dry-run</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Run the full pipeline</h2> bash ultimate-music-manager/scripts/run-pipeline.sh</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Run including Spotify sync</h2> bash ultimate-music-manager/scripts/run-pipeline.sh --spotify</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Check pipeline status at any time</h2> bash ultimate-music-manager/scripts/status.sh </code></pre></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Hook Integration</h3></p><p style="margin:8px 0">A safety guard hook prevents accidental execution of destructive utility scripts.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Setup (Claude Code)</h4></p><p style="margin:8px 0">Add to <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.claude/settings.json</code>:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">{ "hooks": { "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "./ultimate-music-manager/hooks/safety-guard.sh" }] }] } } </code></pre></p><p style="margin:8px 0">The hook intercepts commands referencing <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05D_force_delete_residue</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05F_final_scrub</code>, <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">total_scrub</code>, or <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">absolute_zero_sort</code> and injects a warning requiring explicit user confirmation. Silent (zero overhead) for all other commands.</p><p style="margin:8px 0">See <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">hooks/HOOK.md</code> for details.</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">References</h3></p><p style="margin:8px 0">Detailed documentation for deeper dives:</p><p style="margin:8px 0">| Document | Contents | | -------- | -------- | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">references/data-files.md</code> | Schema and lineage for every JSON artifact β€” which script writes it, which scripts read it, full data flow diagram. | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">references/language-hints-guide.md</code> | How to create language hint files, full schema, examples for Hindi/Japanese/Spanish, tips for getting classification right. |</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Design Principles</h3></p><p style="margin:8px 0">1. <strong style="color:#e5e7eb">Config-driven.</strong> One <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">config.py</code> module, one <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code> file. No hardcoded paths anywhere. 2. <strong style="color:#e5e7eb">Language-agnostic.</strong> No baked-in language lists. <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">langdetect</code> + optional hint files handle any language. 3. <strong style="color:#e5e7eb">Phased and incremental.</strong> Each phase builds on the previous. You can stop after any phase and have a useful result. 4. <strong style="color:#e5e7eb">Auditable.</strong> Every move, merge, and enrichment decision is logged to JSON in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em"><DATA_DIR></code>. 5. <strong style="color:#e5e7eb">Zero-config happy path.</strong> Clone into your music folder, create a <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code> with <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">MUSIC_ROOT</code>, and run. </p></div></section><section class="skill-card" style="margin-bottom:20px"><h2 style="color:#f8fafc;font-size:1.2em;font-weight:800;margin:0 0 16px;display:flex;align-items:center;gap:8px">πŸ’‘ Examples</h2><div style="font-size:.92em;color:#94a3b8;line-height:1.75"><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"># Check environment is ready bash ultimate-music-manager/scripts/preflight.sh</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Preview what the pipeline will do</h2> bash ultimate-music-manager/scripts/run-pipeline.sh --dry-run</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Run the full pipeline</h2> bash ultimate-music-manager/scripts/run-pipeline.sh</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Run including Spotify sync</h2> bash ultimate-music-manager/scripts/run-pipeline.sh --spotify</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Check pipeline status at any time</h2> bash ultimate-music-manager/scripts/status.sh </code></pre></p></div></section><section class="skill-card" style="margin-bottom:20px"><h2 style="color:#f8fafc;font-size:1.2em;font-weight:800;margin:0 0 16px;display:flex;align-items:center;gap:8px">βš™οΈ Configuration</h2><div style="font-size:.92em;color:#94a3b8;line-height:1.75"><p style="margin:8px 0">Create a <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code> file in the repo root (copy from <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env.example</code>):</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"># REQUIRED β€” absolute path to the root of your music collection MUSIC_ROOT=/path/to/your/music</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">OPTIONAL β€” override any of these defaults:</h2> <h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">SORTED_ROOT=/path/to/your/music/Sorted</h2> <h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">DATA_DIR=/path/to/your/music/.data</h2> <h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">SHAZAM_CONCURRENCY=20</h2> <h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">ITUNES_COUNTRIES=US,GB</h2> </code></pre></p><p style="margin:8px 0">Verify configuration:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">python config.py </code></pre></p><p style="margin:8px 0">This prints all resolved paths and confirms <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">MUSIC_ROOT</code> exists.</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Directory Layout After Setup</h4></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"><MUSIC_ROOT>/ β”œβ”€β”€ sonic-phoenix/ # This repo β”‚ β”œβ”€β”€ config.py # Central configuration (single source of truth) β”‚ β”œβ”€β”€ .env # Your local overrides (gitignored) β”‚ β”œβ”€β”€ 01A_extract_metadata.py β”‚ β”œβ”€β”€ 01D_shazam_all_files.py β”‚ β”œβ”€β”€ ... β”‚ └── config/ β”‚ └── language_hints/ # Optional language classification overrides β”‚ └── examples/ # Templates to copy and customise β”œβ”€β”€ Sorted/ # Pipeline output (auto-created) β”‚ β”œβ”€β”€ English/ β”‚ β”‚ β”œβ”€β”€ Adele/ β”‚ β”‚ β”‚ └── 25/ β”‚ β”‚ β”‚ └── Adele - Hello.mp3 β”‚ β”‚ └── ... β”‚ β”œβ”€β”€ Hindi/ β”‚ └── <any language langdetect finds>/ β”œβ”€β”€ .data/ # Machine-generated JSON artifacts (auto-created) β”‚ β”œβ”€β”€ metadata_catalog.json β”‚ β”œβ”€β”€ shazam_final_results.json β”‚ β”œβ”€β”€ catalog.json β”‚ β”œβ”€β”€ final_catalog.json β”‚ └── ... └── <your unsorted audio files> </code></pre></p></div></section><section class="skill-card" style="margin-bottom:20px"><h2 style="color:#f8fafc;font-size:1.2em;font-weight:800;margin:0 0 16px;display:flex;align-items:center;gap:8px">πŸ“‹ Tips & Best Practices</h2><div style="font-size:.92em;color:#94a3b8;line-height:1.75"><p style="margin:8px 0">| Problem | Fix | |---------|-----| | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">shazamio-core</code> fails to install | You're on Python 3.13+. Use Python 3.12: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">python3.12 -m venv .venv</code> | | Shazam returns HTTP 429 | Lower <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">SHAZAM_CONCURRENCY</code> to 5-10 in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code> | | iTunes returns 403 | Add more country codes: <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">ITUNES_COUNTRIES=US,GB,AU,CA,DE</code> | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">MUSIC_ROOT does not exist</code> | Set <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">MUSIC_ROOT</code> in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">.env</code> to the absolute path of your music folder | | Tags still show junk after enrichment | Re-run <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">04I_polish_and_enrich_v6.py</code> β€” it's idempotent | | Spotify 403 on playlist creation | Add your Spotify account under "Users and Access" in the Developer Dashboard (Development Mode restriction) | | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">langdetect</code> misclassifies a language | Create a hint file in <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">config/language_hints/</code> for that language | | Empty artist/album folders after sorting | Run <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05E_final_cleanup.py</code> or <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">05H_final_vacuum.py</code> to vacuum empties |</p></div></section></div><div class="two-col-side"></div></div></div><script> document.querySelectorAll('.copy-btn, .script-copy-btn').forEach(btn => { btn.addEventListener('click', () => { const cmd = btn.getAttribute('data-cmd'); if (!cmd) return; navigator.clipboard.writeText(cmd).then(() => { const orig = btn.textContent; btn.textContent = 'Copied!'; setTimeout(() => btn.textContent = orig, 1500); }).catch(() => {}); }); }); </script><!--$--><!--/$--></main><footer style="background:var(--bg-primary);border-top:1px solid var(--border-secondary);margin-top:60px"><div style="border-top:1px solid var(--border-light);max-width:1200px;margin:0 auto;padding:24px 20px"><div style="display:flex;justify-content:space-between;flex-wrap:wrap;gap:24px;margin-bottom:24px"><div><div style="font-weight:700;color:var(--text-muted);margin-bottom:8px">BytesAgain</div><div style="color:var(--text-muted3);font-size:.82em;max-width:200px">Discover the best AI agent skills for your workflow.</div></div><div><div style="color:var(--text-muted);font-size:.75em;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px">Explore</div><div style="margin-bottom:6px"><a href="/skills" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Skills</a></div><div style="margin-bottom:6px"><a href="/articles" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Articles</a></div><div style="margin-bottom:6px"><a href="/use-case" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Cases</a></div></div><div><div style="color:var(--text-muted);font-size:.75em;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px">Company</div><div style="margin-bottom:6px"><a href="/about" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">About</a></div><div style="margin-bottom:6px"><a href="/contact" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Contact</a></div><div style="margin-bottom:6px"><a href="/privacy-policy" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Privacy Policy</a></div><div style="margin-bottom:6px"><a href="/terms" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Terms</a></div><div style="margin-bottom:6px"><a href="/feedback" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Feedback</a></div></div></div><div style="border-top:1px solid var(--border-light);padding-top:16px"><div style="color:var(--text-muted4);font-size:.8em;margin-bottom:8px">Β© <!-- -->2026<!-- --> BytesAgain. All rights reserved.</div><div style="color:var(--text-muted5);font-size:.75em;line-height:1.6;max-width:720px">BytesAgain is an independent skill directory. We index and link to third-party content (ClawHub, GitHub, LobeHub, Dify, etc.) for informational purposes only. All trademarks, skill names, and content are the property of their respective owners. BytesAgain does not claim ownership of any indexed content.</div></div></div></footer><button style="position:fixed;bottom:28px;right:28px;z-index:1000;width:48px;height:48px;border-radius:50%;border:none;cursor:pointer;background:linear-gradient(135deg,#667eea,#00d4ff);color:#fff;font-size:1.3em;box-shadow:0 4px 20px #667eea66;display:flex;align-items:center;justify-content:center;transition:transform .2s">πŸ’¬</button><script src="/_next/static/chunks/0ze4gu236oq96.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[62894,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"LangProvider\"]\n3:I[89220,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"ThemeProvider\"]\n4:I[16988,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\ne:I[68027,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\",1]\n:HL[\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"style\"]\n:HL[\"/_next/static/media/caa3a2e1cccd8315-s.p.09~u27dqhyhd6.woff2?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n5:Td5e,"])</script><script>self.__next_f.push([1,"[{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"BytesAgain\",\"url\":\"https://bytesagain.com\",\"description\":\"Search 60,000+ verified AI agent skills via MCP API or REST. Supports 7 languages. Free, no auth required.\",\"inLanguage\":[\"en\",\"zh\",\"es\",\"fr\",\"de\",\"ja\",\"ko\"],\"potentialAction\":{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https://bytesagain.com/skills?q={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}},{\"@context\":\"https://schema.org\",\"@type\":\"Organization\",\"name\":\"BytesAgain\",\"url\":\"https://bytesagain.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://bytesagain.com/og-image.png\"},\"description\":\"AI agent skill directory. Search 60,000+ skills, 1,000+ use cases, and community requests.\",\"foundingDate\":\"2026\",\"foundingLocation\":{\"@type\":\"Place\",\"name\":\"Global\"},\"sameAs\":[\"https://x.com/bytesagain\",\"https://github.com/bytesagain/ai-skills\",\"https://clawhub.ai/profile/bytesagain\"],\"contactPoint\":{\"@type\":\"ContactPoint\",\"email\":\"hello@bytesagain.com\",\"contactType\":\"customer support\"},\"numberOfEmployees\":{\"@type\":\"QuantitativeValue\",\"value\":1}},{\"@context\":\"https://schema.org\",\"@type\":\"WebApplication\",\"name\":\"BytesAgain AI Skills Search\",\"url\":\"https://bytesagain.com\",\"applicationCategory\":\"DeveloperApplication\",\"operatingSystem\":\"Web\",\"description\":\"Search engine and MCP API for 60,000+ AI agent skills. Semantic search, role recommendations, and use case packs.\",\"offers\":{\"@type\":\"Offer\",\"price\":\"0\",\"priceCurrency\":\"USD\"},\"featureList\":[\"Search 60,000+ AI agent skills\",\"Role-based recommendations for developers, creators, and traders\",\"1,000+ curated use case packs\",\"Free MCP API and REST API\",\"Multi-language search (EN, ZH, ES, FR, DE, JA, KO)\"],\"potentialAction\":{\"@type\":\"SearchAction\",\"target\":\"https://bytesagain.com/skills?q={search_term_string}\",\"query-input\":\"required name=search_term_string\"},\"dateModified\":\"2026-07-17\"},{\"@context\":\"https://schema.org\",\"@type\":\"FAQPage\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"What is BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"BytesAgain is a curated directory of 60,000+ AI agent skills from ClawHub, GitHub, LobeHub, and Dify. Search skills by keyword in 7 languages, browse by role (developer, creator, trader, marketer) or by use case.\"}},{\"@type\":\"Question\",\"name\":\"How do I find AI skills on BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Use the search bar on BytesAgain.com to search by keyword in 7 languages. You can also browse by role (developer, creator, trader, marketer) or by use case. Each skill shows install instructions for Claude, Cursor, OpenClaw, Continue, and more.\"}},{\"@type\":\"Question\",\"name\":\"Is BytesAgain free?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes, BytesAgain is completely free. No registration required for searching skills. The MCP API is also free with rate limits.\"}},{\"@type\":\"Question\",\"name\":\"Does BytesAgain have an API for AI agents?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes! BytesAgain provides a free MCP SSE endpoint at /api/mcp/sse for AI agents, plus a REST API at /api/mcp?action=search\u0026q=\u003cquery\u003e. No authentication needed.\"}},{\"@type\":\"Question\",\"name\":\"Can I request a new AI skill on BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes! Visit the Requests page on BytesAgain.com to submit a skill request. Your request will be visible to the community and notified to the site admin.\"}}]}]"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"skill\",\"ultimate-music-manager\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"skill\",{\"children\":[[\"slug\",\"ultimate-music-manager\",\"d\",null],{\"children\":[\"__PAGE__\",{}]}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-2\",{\"src\":\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"link\",null,{\"rel\":\"llms\",\"href\":\"/llms.txt\"}],[\"$\",\"link\",null,{\"rel\":\"llms-full\",\"href\":\"/llms-full.txt\"}],[\"$\",\"script\",null,{\"async\":true,\"src\":\"https://www.googletagmanager.com/gtag/js?id=G-3C1MM9FWYF\"}],[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n window.dataLayer = window.dataLayer || [];\\n function gtag(){dataLayer.push(arguments);}\\n gtag('js', new Date());\\n gtag('config', 'G-3C1MM9FWYF');\\n \"}}]]}],[\"$\",\"body\",null,{\"className\":\"geist_9e050971-module__05dp7a__className\",\"style\":{\"margin\":0},\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[[\"$\",\"div\",null,{\"style\":{\"width\":\"100%\",\"background\":\"var(--bg-subscribe)\",\"borderBottom\":\"1px solid var(--border-primary)\",\"padding\":\"8px 20px\",\"textAlign\":\"center\",\"fontSize\":\".82em\",\"color\":\"#818cf8\"},\"children\":[\"🎁 \",[\"$\",\"strong\",null,{\"style\":{\"color\":\"var(--text-primary)\"},\"children\":\"Get the FREE AI Skills Starter Guide\"}],\" β€” \",[\"$\",\"a\",null,{\"href\":\"/register\",\"style\":{\"color\":\"#00d4ff\",\"textDecoration\":\"underline\"},\"children\":\"Subscribe β†’\"}]]}],[\"$\",\"$L4\",null,{}],[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"$5\"}}],\"$L6\",\"$L7\",\"$L8\"]}]}]}]]}]]}],{\"children\":[\"$L9\",{\"children\":[\"$La\",{\"children\":[\"$Lb\",{},null,false,null]},null,false,\"$@c\"]},null,false,\"$@c\"]},null,false,null],\"$Ld\",false]],\"m\":\"$undefined\",\"G\":[\"$e\",[\"$Lf\"]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"10:I[39756,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n11:I[37457,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n12:I[22016,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0ka051yepewro.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"\"]\n13:I[90940,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n14:I[16397,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n16:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"OutletBoundary\"]\n17:\"$Sreact.suspense\"\n1a:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"ViewportBoundary\"]\n1c:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"MetadataBoundary\"]\n"])</script><script>self.__next_f.push([1,"6:[\"$\",\"main\",null,{\"children\":[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"main\",null,{\"style\":{\"minHeight\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"background\":\"#050611\",\"color\":\"#e5e7eb\"},\"children\":[[\"$\",\"style\",null,{\"children\":\"\\n .nf-box { text-align: center; padding: 60px 32px; }\\n .nf-code { font-size: 6rem; font-weight: 900; color: #22d3ee; line-height: 1; margin: 0; }\\n .nf-title { font-size: 1.8rem; font-weight: 800; margin: 12px 0 8px; }\\n .nf-desc { color: var(--text-muted2); font-size: 1rem; margin-bottom: 32px; max-width: 440px; }\\n .nf-link { display: inline-block; padding: 12px 28px; background: linear-gradient(135deg,#34d399,#22d3ee); color: #000; font-weight: 900; border-radius: 12px; text-decoration: none; }\\n \"}],[\"$\",\"div\",null,{\"className\":\"nf-box\",\"children\":[[\"$\",\"p\",null,{\"className\":\"nf-code\",\"children\":\"404\"}],[\"$\",\"h1\",null,{\"className\":\"nf-title\",\"children\":\"Page Not Found\"}],[\"$\",\"p\",null,{\"className\":\"nf-desc\",\"children\":\"The skill or page you're looking for doesn't exist or has been moved.\"}],[\"$\",\"$L12\",null,{\"className\":\"nf-link\",\"href\":\"/\",\"children\":\"Back to BytesAgain\"}]]}]]}],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]\n"])</script><script>self.__next_f.push([1,"7:[\"$\",\"$L13\",null,{}]\n8:[\"$\",\"$L14\",null,{}]\n9:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\na:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\nb:[\"$\",\"$1\",\"c\",{\"children\":[\"$L15\",[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L16\",null,{\"children\":[\"$\",\"$17\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@18\"}]}]]}]\n19:[]\nc:\"$W19\"\nd:[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L1a\",null,{\"children\":\"$L1b\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L1c\",null,{\"children\":[\"$\",\"$17\",null,{\"name\":\"Next.Metadata\",\"children\":\"$L1d\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}]\nf:[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]\n"])</script><script>self.__next_f.push([1,"1b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"1e:I[27201,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"IconMark\"]\n18:null\n"])</script><script>self.__next_f.push([1,"1d:[[\"$\",\"title\",\"0\",{\"children\":\"Ultimate Music Manager β€” AI Agent Skill | BytesAgain | BytesAgain\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Organises a messy local music library into a clean Language/Artist/Album hierarchy using acoustic fingerprinting, deduplication, metadata enrichment, and opt...\"}],[\"$\",\"meta\",\"2\",{\"name\":\"robots\",\"content\":\"index, follow\"}],[\"$\",\"meta\",\"3\",{\"name\":\"googlebot\",\"content\":\"index, follow, max-image-preview:large, max-snippet:-1\"}],[\"$\",\"meta\",\"4\",{\"name\":\"llms-txt\",\"content\":\"https://bytesagain.com/llms.txt\"}],[\"$\",\"meta\",\"5\",{\"name\":\"llms-full-txt\",\"content\":\"https://bytesagain.com/llms-full.txt\"}],[\"$\",\"link\",\"6\",{\"rel\":\"canonical\",\"href\":\"https://bytesagain.com/skill/ultimate-music-manager\"}],[\"$\",\"meta\",\"7\",{\"name\":\"baidu-site-verification\",\"content\":\"codeva-0evUqX1TFs\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:title\",\"content\":\"Ultimate Music Manager β€” AI Agent Skill | BytesAgain\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:description\",\"content\":\"Organises a messy local music library into a clean Language/Artist/Album hierarchy using acoustic fingerprinting, deduplication, metadata enrichment, and opt...\"}],[\"$\",\"meta\",\"10\",{\"property\":\"og:url\",\"content\":\"https://bytesagain.com/skill/ultimate-music-manager\"}],[\"$\",\"meta\",\"11\",{\"property\":\"og:site_name\",\"content\":\"BytesAgain\"}],[\"$\",\"meta\",\"12\",{\"property\":\"og:image\",\"content\":\"https://bytesagain.com/social-preview.png\"}],[\"$\",\"meta\",\"13\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"14\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"15\",{\"property\":\"og:type\",\"content\":\"website\"}],[\"$\",\"meta\",\"16\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"17\",{\"name\":\"twitter:title\",\"content\":\"Ultimate Music Manager β€” AI Agent Skill | BytesAgain\"}],[\"$\",\"meta\",\"18\",{\"name\":\"twitter:description\",\"content\":\"Organises a messy local music library into a clean Language/Artist/Album hierarchy using acoustic fingerprinting, deduplication, metadata enrichment, and opt...\"}],[\"$\",\"meta\",\"19\",{\"name\":\"twitter:image\",\"content\":\"https://bytesagain.com/social-preview.png\"}],[\"$\",\"meta\",\"20\",{\"name\":\"twitter:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"21\",{\"name\":\"twitter:image:height\",\"content\":\"630\"}],[\"$\",\"link\",\"22\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0x3dzn~oxb6tn.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L1e\",\"23\",{}]]\n"])</script><script>self.__next_f.push([1,"1f:T1562,"])</script><script>self.__next_f.push([1,"\n .skill-page { max-width: 1100px; margin: 0 auto; padding: 32px 20px 80px; }\n .two-col { display: flex; gap: 32px; align-items: flex-start; }\n .two-col-main { flex: 1; min-width: 0; }\n .two-col-side { width: 300px; flex-shrink: 0; }\n @media (max-width: 860px) {\n .two-col { flex-direction: column; }\n .two-col-side { width: 100%; }\n }\n .breadcrumb { font-size: .82em; color: var(--text-muted2); margin-bottom: 28px; }\n .breadcrumb a { color: #818cf8; text-decoration: none; }\n .breadcrumb a:hover { text-decoration: underline; }\n .skill-card { background: var(--bg-card); border: 1px solid var(--border-card); border-radius: 20px; padding: 28px; margin-bottom: 24px; }\n .skill-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }\n .skill-badges { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n .skill-top-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }\n .badge { display: inline-flex; align-items: center; gap: 5px; font-size: .75em; font-weight: 600; padding: 4px 12px; border-radius: 999px; border: 1px solid transparent; }\n .skill-title { font-size: 1.6em; font-weight: 800; color: var(--text-primary); margin: 0 0 4px; line-height: 1.2; }\n .skill-owner { font-size: .82em; color: var(--text-muted2); margin: 0 0 14px; }\n .skill-owner span { color: #818cf8; }\n .skill-desc { font-size: .92em; color: var(--text-secondary); line-height: 1.65; margin: 0 0 16px; }\n .skill-meta { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; padding-bottom: 16px; border-bottom: 1px solid var(--border-card); }\n .meta-item { display: flex; flex-direction: column; gap: 2px; }\n .meta-label { font-size: .7em; color: var(--text-muted5); text-transform: uppercase; letter-spacing: 1px; font-weight: 600; }\n .meta-value { font-size: .92em; color: var(--text-muted2); font-weight: 600; }\n .tags-row { display: flex; gap: 6px; flex-wrap: wrap; }\n .tag { font-size: .75em; color: #6366f1; background: #6366f115; border: 1px solid #6366f130; border-radius: 6px; padding: 3px 10px; text-decoration: none; }\n .tag:hover { background: #6366f125; }\n .install-box { background: var(--bg-deep); border: 1px solid var(--border-card); border-radius: 12px; overflow: hidden; margin-bottom: 24px; }\n .install-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; border-bottom: 1px solid var(--border-card); }\n .install-dots { display: flex; gap: 6px; }\n .dot { width: 10px; height: 10px; border-radius: 50%; }\n .install-label { font-size: .72em; color: var(--text-muted5); font-family: monospace; letter-spacing: 1px; }\n .install-body { padding: 16px 20px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }\n .install-cmd { color: var(--text-code);\n font-family: 'Courier New', monospace; font-size: 1em; }\n .copy-btn { font-size: .75em; color: #6366f1; background: #6366f115; border: 1px solid #6366f130; border-radius: 6px; padding: 5px 12px; cursor: pointer; white-space: nowrap; transition: all .15s; }\n .copy-btn:hover { background: #6366f125; }\n .btn-secondary { display: inline-flex; align-items: center; gap: 8px; padding: 13px 24px; background: transparent; border: 1px solid var(--border-card); border-radius: 10px; color: #6b7280; text-decoration: none; font-weight: 600; font-size: .95em; transition: all .15s; }\n .btn-secondary:hover { border-color: #818cf8; color: #818cf8; }\n .ours-badge { display: inline-flex; align-items: center; gap: 6px; font-size: .72em; font-weight: 700; color: #22d3ee; background: #22d3ee10; border: 1px solid #22d3ee30; border-radius: 999px; padding: 4px 14px; }\n .section-card { background: var(--bg-card); border: 1px solid var(--border-card); border-radius: 16px; padding: 22px 24px; margin-bottom: 20px; }\n .section-title { color: var(--text-primary); font-size: 1.08em; font-weight: 800; margin: 0 0 12px; display: flex; align-items: center; gap: 8px; }\n /* Script box */\n .script-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 14px; background: var(--bg-input); border-bottom: 1px solid var(--border-card); }\n .script-filename { font-size: .72em; color: var(--text-muted2); font-family: 'Courier New', monospace; }\n .script-copy-btn { font-size: .72em; color: #6366f1; background: none; border: 1px solid #6366f130; border-radius: 4px; padding: 2px 10px; cursor: pointer; }\n .script-copy-btn:hover { background: #6366f115; }\n .script-body { padding: 14px 16px; font-family: 'Courier New', monospace; font-size: .82em; line-height: 1.6; color: var(--text-code); overflow-x: auto; max-height: 420px; overflow-y: auto; white-space: pre; }\n /* Articles */\n .article-card { display: block; background: var(--bg-secondary); border: 1px solid var(--border-primary); border-radius: 10px; padding: 14px 16px; text-decoration: none; transition: border-color .15s; }\n .article-card:hover { border-color: #6366f1; }\n @media (max-width: 600px) {\n .skill-card { padding: 20px; }\n .skill-title { font-size: 1.5em; }\n }\n "])</script><script>self.__next_f.push([1,"15:[[\"$\",\"style\",null,{\"children\":\"$1f\"}],\"$L20\",\"$L21\"]\n"])</script><script>self.__next_f.push([1,"22:I[78297,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n23:T8d26,"])</script><script>self.__next_f.push([1,"\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\nname: ultimate-music-manager\ndescription: \"Organises a messy local music library into a clean Language/Artist/Album hierarchy using acoustic fingerprinting, deduplication, metadata enrichment, and optional Spotify sync. Use when: (1) User wants to sort or clean up a folder of audio files, (2) User has untagged or badly-tagged MP3/FLAC/M4A files, (3) User wants to identify unknown songs via Shazam fingerprinting, (4) User wants to deduplicate audio files by content hash, (5) User wants to enrich metadata with iTunes artwork and LrcLib lyrics, (6) User wants to sync their local library to Spotify playlists.\"\nmetadata:\n openclaw:\n requires:\n env:\n - MUSIC_ROOT\n optionalEnv:\n - SPOTIFY_CLIENT_ID\n - SPOTIFY_CLIENT_SECRET\n - SPOTIFY_REDIRECT_URI\n - SHAZAM_CONCURRENCY\n - ITUNES_COUNTRIES\n - SORTED_ROOT\n - DATA_DIR\n - FFMPEG_BIN\n bins:\n - python3.12\n - git\n optionalBins:\n - ffmpeg\n primaryEnv: MUSIC_ROOT\n source: https://github.com/drajb/sonic-phoenix\n\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eSonic Phoenix\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eA multi-phase pipeline that transforms a disorganised local music collection into a pristine, fully-tagged library sorted by \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003cLanguage\u003e/\u003cArtist\u003e/\u003cAlbum\u003e/\u003cArtist\u003e - \u003cTitle\u003e.\u003cext\u003e\u003c/code\u003e. Works with any language β€” Hindi, English, Japanese, Spanish, or anything else your collection contains.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe pipeline never deletes audio files. Suspected duplicates are moved to a staging folder for manual review.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eQuick Reference\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Situation | Action |\n|-----------|--------|\n| First-time setup | \u003ca href=\"#environment-setup\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003eEnvironment Setup\u003c/a\u003e |\n| Sort a messy music folder | Run Phases 1-5 in order |\n| Identify unknown songs | Run Phase 1 (Shazam fingerprinting) |\n| Find and remove duplicates | Run Phase 2 (SHA-256 catalog) |\n| Fix bad/missing ID3 tags | Run Phase 4 (iTunes enrichment) |\n| Sync library to Spotify | Run Phase 6 (requires Spotify app credentials) |\n| Add language classification hints | Create hint files in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003econfig/language_hints/\u003c/code\u003e |\n| Check current configuration | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003epython config.py\u003c/code\u003e |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eEnvironment Setup\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ePrerequisites\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003ePython 3.12\u003c/strong\u003e (required β€” \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eshazamio-core\u003c/code\u003e only ships wheels for 3.10-3.12)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eGit\u003c/strong\u003e (to clone the repo)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eFFmpeg\u003c/strong\u003e (optional β€” only needed if your collection contains non-MP3 formats like FLAC, OGG, or WMA that Shazam needs to decode)\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eInstallation\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Clone into your music folder (recommended) or anywhere else\ncd /path/to/your/music\ngit clone https://github.com/drajb/sonic-phoenix.git\ncd sonic-phoenix\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eCreate virtual environment with Python 3.12\u003c/h2\u003e\npython3.12 -m venv .venv\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eActivate\u003c/h2\u003e\nsource .venv/bin/activate # macOS / Linux\n\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003e.venv\\Scripts\\activate # Windows PowerShell\u003c/h2\u003e\n\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003e.venv\\Scripts\\activate.bat # Windows cmd\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eInstall dependencies\u003c/h2\u003e\npip install -r requirements.txt\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eConfiguration\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eCreate a \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e file in the repo root (copy from \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env.example\u003c/code\u003e):\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# REQUIRED β€” absolute path to the root of your music collection\nMUSIC_ROOT=/path/to/your/music\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eOPTIONAL β€” override any of these defaults:\u003c/h2\u003e\n\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eSORTED_ROOT=/path/to/your/music/Sorted\u003c/h2\u003e\n\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eDATA_DIR=/path/to/your/music/.data\u003c/h2\u003e\n\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eSHAZAM_CONCURRENCY=20\u003c/h2\u003e\n\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eITUNES_COUNTRIES=US,GB\u003c/h2\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eVerify configuration:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003epython config.py\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThis prints all resolved paths and confirms \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eMUSIC_ROOT\u003c/code\u003e exists.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eDirectory Layout After Setup\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e\u003cMUSIC_ROOT\u003e/\nβ”œβ”€β”€ sonic-phoenix/ # This repo\nβ”‚ β”œβ”€β”€ config.py # Central configuration (single source of truth)\nβ”‚ β”œβ”€β”€ .env # Your local overrides (gitignored)\nβ”‚ β”œβ”€β”€ 01A_extract_metadata.py\nβ”‚ β”œβ”€β”€ 01D_shazam_all_files.py\nβ”‚ β”œβ”€β”€ ...\nβ”‚ └── config/\nβ”‚ └── language_hints/ # Optional language classification overrides\nβ”‚ └── examples/ # Templates to copy and customise\nβ”œβ”€β”€ Sorted/ # Pipeline output (auto-created)\nβ”‚ β”œβ”€β”€ English/\nβ”‚ β”‚ β”œβ”€β”€ Adele/\nβ”‚ β”‚ β”‚ └── 25/\nβ”‚ β”‚ β”‚ └── Adele - Hello.mp3\nβ”‚ β”‚ └── ...\nβ”‚ β”œβ”€β”€ Hindi/\nβ”‚ └── \u003cany language langdetect finds\u003e/\nβ”œβ”€β”€ .data/ # Machine-generated JSON artifacts (auto-created)\nβ”‚ β”œβ”€β”€ metadata_catalog.json\nβ”‚ β”œβ”€β”€ shazam_final_results.json\nβ”‚ β”œβ”€β”€ catalog.json\nβ”‚ β”œβ”€β”€ final_catalog.json\nβ”‚ └── ...\n└── \u003cyour unsorted audio files\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eThe Pipeline\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eHappy Path (Minimum Viable Run)\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eFor a quick, effective sort of your library, run these scripts in order:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003epython 01A_extract_metadata.py # Extract ID3/FLAC tags\npython 01D_shazam_all_files.py # Acoustic fingerprint every file via Shazam\npython 02A_catalog_music.py # SHA-256 hash catalog + duplicate detection\npython 02D_organize_music.py # Sort into Language/Artist/Album hierarchy\npython 03A_consolidate_by_artist.py --dry-run # Preview artist folder merges\npython 03A_consolidate_by_artist.py # Execute merges\npython 03D_titanium_resort.py # Final structural enforcement pass\npython 04I_polish_and_enrich_v6.py # Enrich tags via iTunes + embed artwork\npython 05I_finalize_catalog.py # Lock the master catalog\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eEach script is resumable β€” if interrupted, re-run it and it picks up where it left off.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ePhase 1: Extraction and Identification\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Script | Status | Purpose |\n|--------|--------|---------|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e01A_extract_metadata.py\u003c/code\u003e | CANONICAL | Reads existing ID3/FLAC/MP4 tags via Mutagen. Splits files into \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003etagged_files\u003c/code\u003e and \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003euntagged_files\u003c/code\u003e in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003emetadata_catalog.json\u003c/code\u003e. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e01B_shazam_identify.py\u003c/code\u003e | UTILITY | Shazam-identify only the untagged files from 01A. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e01C_shazam_by_hash.py\u003c/code\u003e | UTILITY | Propagate a single Shazam match to all byte-identical copies (via hash groups from Phase 2). |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e01D_shazam_all_files.py\u003c/code\u003e | CANONICAL | Force every audio file through Shazam regardless of existing tags. Use this when tags are unreliable. 20-way concurrent by default. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e01E_test_matching.py\u003c/code\u003e | UTILITY | Spot-check a handful of files against Shazam to verify the pipeline is working. |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eWhen to use 01B vs 01D:\u003c/strong\u003e If your collection has clean tags on most files, use 01A then 01B (faster β€” only fingerprints untagged files). If tags are unreliable (generic names like \"Track 01\", meaningless album fields, garbled metadata), use 01D to fingerprint everything and cross-validate against the acoustic results.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eShazam rate limiting:\u003c/strong\u003e If you see HTTP 429 errors, lower \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eSHAZAM_CONCURRENCY\u003c/code\u003e in your \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e (try 5-10).\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ePhase 2: Cataloguing and Deduplication\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Script | Status | Purpose |\n|--------|--------|---------|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e02A_catalog_music.py\u003c/code\u003e | CANONICAL | SHA-256 hash every audio file. Writes \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ecatalog.json\u003c/code\u003e with duplicate groups. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e02B_analyze_catalog.py\u003c/code\u003e | UTILITY | Analyses the hash catalog and prints duplicate statistics. Read-only reporter. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e02C_organize_files.py\u003c/code\u003e | UTILITY | Organises files using the hash catalog and final catalog. Alternative to 02D. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e02D_organize_music.py\u003c/code\u003e | CANONICAL | The main sorter. Reads Shazam results and moves files into \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003cSORTED_ROOT\u003e/\u003cLanguage\u003e/\u003cArtist\u003e/\u003cAlbum\u003e/\u003c/code\u003e. |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ePhase 3: Consolidation and Structural Cleanup\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Script | Status | Purpose |\n|--------|--------|---------|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e03A_consolidate_by_artist.py\u003c/code\u003e | UTILITY | Merge fragmented artist folders (e.g. \"Akon feat Eminem\" into \"Akon\"). Supports \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--dry-run\u003c/code\u003e. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e03D_titanium_resort.py\u003c/code\u003e | CANONICAL | Final structural enforcer. Any file not matching \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eLanguage/Artist/Album/\u003c/code\u003e gets re-sorted. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e03F_reorganize_binary.py\u003c/code\u003e | UTILITY | Resolve near-duplicate files via binary comparison. |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ePhase 4: Metadata Enrichment\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Script | Status | Purpose |\n|--------|--------|---------|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e04I_polish_and_enrich_v6.py\u003c/code\u003e | CANONICAL | The single enrichment entry point. Cleans junk from tags (quality markers, description artifacts, miscellaneous noise), enriches via iTunes Search API (artist, title, album, release date, artwork), fetches synchronized lyrics from LrcLib, embeds cover art into ID3 tags. |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eiTunes rate limiting:\u003c/strong\u003e The script rotates queries across country codes (default: US, GB). Add more via \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eITUNES_COUNTRIES=US,GB,AU,CA\u003c/code\u003e in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ePhase 5: Finalization\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Script | Status | Purpose |\n|--------|--------|---------|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05I_finalize_catalog.py\u003c/code\u003e | CANONICAL | Merges ID3 tags, Shazam results, and NLP language classification into \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003efinal_catalog.json\u003c/code\u003e. This is the read-only master snapshot of the library. |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eSupporting utilities: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05A\u003c/code\u003e (JSON repair), \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05B\u003c/code\u003e (sanitize results), \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05C\u003c/code\u003e (confidence audit), \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05D\u003c/code\u003e (residue purge), \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05E\u003c/code\u003e/\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05H\u003c/code\u003e (empty-folder vacuum).\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ePhase 6: Spotify Sync (Optional)\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eRequires a free Spotify Developer account.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Script | Status | Purpose |\n|--------|--------|---------|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e06B_spotify_setup.py\u003c/code\u003e | CANONICAL | OAuth handshake β€” run once to cache the token. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e06C_spotify_backup.py\u003c/code\u003e | CANONICAL | Snapshot all existing playlists before making changes. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e06D_spotify_sync_engine.py\u003c/code\u003e | CANONICAL | Creates per-language playlists mirroring your local library. Resumable. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e06E_spotify_discovery_sync.py\u003c/code\u003e | CANONICAL | Cross-references local artists with Spotify listening history and generates genre-based \"Essentials\" playlists. |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSpotify setup:\u003c/strong\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. Create an app at https://developer.spotify.com/dashboard\n2. Set Redirect URI to \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ehttp://127.0.0.1:8888/callback\u003c/code\u003e\n3. Add to \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e:\n \u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e SPOTIFY_CLIENT_ID=your_client_id\n SPOTIFY_CLIENT_SECRET=your_client_secret\n \u003c/code\u003e\u003c/pre\u003e\n4. Run \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003epython 06B_spotify_setup.py\u003c/code\u003e β€” browser opens, approve scopes, token is cached.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eLanguage Classification\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe pipeline uses \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003elangdetect\u003c/code\u003e (NLP) to classify tracks by language automatically. No hardcoded language list β€” it creates whatever folders your collection needs.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eLanguage Hints (Optional)\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eFor artists that NLP can't classify from Latin-script titles (e.g. Bollywood artists romanised as \"Arijit Singh\"), create hint files:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003econfig/language_hints/\nβ”œβ”€β”€ examples/ # Copy and customise these templates\nβ”‚ β”œβ”€β”€ English.json\nβ”‚ β”œβ”€β”€ Hindi.json\nβ”‚ └── README.md # Full schema reference\nβ”œβ”€β”€ Hindi.json # Your custom hints (create from examples)\n└── Spanish.json\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eHint file schema:\u003c/strong\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e{\n \"artists\": [\"Arijit Singh\", \"A.R. Rahman\", \"Shreya Ghoshal\"],\n \"dna\": [\"bollywood\", \"bhangra\", \"desi\"],\n \"keywords\": [\"ishq\", \"dil\", \"pyaar\"],\n \"lang_codes\": [\"hi\", \"pa\"]\n}\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eartists\u003c/code\u003e β€” exact artist names to classify into this language\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003edna\u003c/code\u003e β€” substrings to match in folder/file paths\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ekeywords\u003c/code\u003e β€” substrings to match in track titles\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003elang_codes\u003c/code\u003e β€” ISO 639-1 codes that \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003elangdetect\u003c/code\u003e might return for this language\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eConfiguration Reference\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eAll configuration lives in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003econfig.py\u003c/code\u003e and is driven by environment variables (set in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e or your shell).\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Variable | Required | Default | Purpose |\n|----------|----------|---------|---------|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eMUSIC_ROOT\u003c/code\u003e | Yes | Parent of repo dir | Root of your music collection |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eSORTED_ROOT\u003c/code\u003e | No | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003cMUSIC_ROOT\u003e/Sorted\u003c/code\u003e | Destination for sorted files |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eDATA_DIR\u003c/code\u003e | No | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003cMUSIC_ROOT\u003e/.data\u003c/code\u003e | Machine-generated JSON artifacts |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eDUPLICATES_STAGING\u003c/code\u003e | No | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003crepo\u003e/Duplicates_Staging\u003c/code\u003e | Quarantine for suspected duplicates |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eUNIDENTIFIED_DIR\u003c/code\u003e | No | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003cSORTED_ROOT\u003e/Unidentified\u003c/code\u003e | Files Shazam couldn't identify |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eFFMPEG_BIN\u003c/code\u003e | No | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003cMUSIC_ROOT\u003e/ffmpeg/bin\u003c/code\u003e | Portable FFmpeg path (Windows) |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eSHAZAM_CONCURRENCY\u003c/code\u003e | No | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e20\u003c/code\u003e | Parallel Shazam calls (lower if rate-limited) |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eITUNES_COUNTRIES\u003c/code\u003e | No | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eUS,GB\u003c/code\u003e | Country codes for iTunes API rotation |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eSPOTIFY_CLIENT_ID\u003c/code\u003e | Phase 6 only | β€” | Spotify app client ID |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eSPOTIFY_CLIENT_SECRET\u003c/code\u003e | Phase 6 only | β€” | Spotify app client secret |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eSPOTIFY_REDIRECT_URI\u003c/code\u003e | No | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ehttp://127.0.0.1:8888/callback\u003c/code\u003e | Spotify OAuth redirect |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eSupported Audio Formats\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.mp3\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.m4a\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.flac\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.wav\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.ogg\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.wma\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.aac\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.opus\u003c/code\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eSafety Guarantees\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNo deletion.\u003c/strong\u003e Audio files are never deleted. Duplicates are moved to \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eDuplicates_Staging/\u003c/code\u003e for manual review.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNo writes into the music hierarchy.\u003c/strong\u003e All working state lives in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003cDATA_DIR\u003e\u003c/code\u003e or the repo directory.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eResumable.\u003c/strong\u003e Every script can be interrupted and re-run safely.\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eNon-destructive tags.\u003c/strong\u003e Enrichment writes new tags but does not remove existing valid data.\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eTroubleshooting\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Problem | Fix |\n|---------|-----|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eshazamio-core\u003c/code\u003e fails to install | You're on Python 3.13+. Use Python 3.12: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003epython3.12 -m venv .venv\u003c/code\u003e |\n| Shazam returns HTTP 429 | Lower \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eSHAZAM_CONCURRENCY\u003c/code\u003e to 5-10 in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e |\n| iTunes returns 403 | Add more country codes: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eITUNES_COUNTRIES=US,GB,AU,CA,DE\u003c/code\u003e |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eMUSIC_ROOT does not exist\u003c/code\u003e | Set \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eMUSIC_ROOT\u003c/code\u003e in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e to the absolute path of your music folder |\n| Tags still show junk after enrichment | Re-run \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e04I_polish_and_enrich_v6.py\u003c/code\u003e β€” it's idempotent |\n| Spotify 403 on playlist creation | Add your Spotify account under \"Users and Access\" in the Developer Dashboard (Development Mode restriction) |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003elangdetect\u003c/code\u003e misclassifies a language | Create a hint file in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003econfig/language_hints/\u003c/code\u003e for that language |\n| Empty artist/album folders after sorting | Run \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05E_final_cleanup.py\u003c/code\u003e or \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05H_final_vacuum.py\u003c/code\u003e to vacuum empties |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eScripts\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eHelper scripts that automate common workflows. Run from the repo root.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Script | Purpose |\n| ------ | ------- |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eultimate-music-manager/scripts/preflight.sh\u003c/code\u003e | Validates Python 3.12, venv, dependencies, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eMUSIC_ROOT\u003c/code\u003e, FFmpeg. Run before first pipeline execution. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eultimate-music-manager/scripts/run-pipeline.sh\u003c/code\u003e | Executes the happy-path sequence (Phases 1-5) with progress and timing. Supports \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--skip-shazam\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--spotify\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e--dry-run\u003c/code\u003e. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eultimate-music-manager/scripts/status.sh\u003c/code\u003e | Dashboard showing file counts, language breakdown, data file status, and pipeline progress. |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eUsage\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Check environment is ready\nbash ultimate-music-manager/scripts/preflight.sh\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003ePreview what the pipeline will do\u003c/h2\u003e\nbash ultimate-music-manager/scripts/run-pipeline.sh --dry-run\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eRun the full pipeline\u003c/h2\u003e\nbash ultimate-music-manager/scripts/run-pipeline.sh\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eRun including Spotify sync\u003c/h2\u003e\nbash ultimate-music-manager/scripts/run-pipeline.sh --spotify\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eCheck pipeline status at any time\u003c/h2\u003e\nbash ultimate-music-manager/scripts/status.sh\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eHook Integration\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eA safety guard hook prevents accidental execution of destructive utility scripts.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eSetup (Claude Code)\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eAdd to \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.claude/settings.json\u003c/code\u003e:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e{\n \"hooks\": {\n \"PreToolUse\": [{\n \"matcher\": \"Bash\",\n \"hooks\": [{\n \"type\": \"command\",\n \"command\": \"./ultimate-music-manager/hooks/safety-guard.sh\"\n }]\n }]\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThe hook intercepts commands referencing \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05D_force_delete_residue\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05F_final_scrub\u003c/code\u003e, \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003etotal_scrub\u003c/code\u003e, or \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eabsolute_zero_sort\u003c/code\u003e and injects a warning requiring explicit user confirmation. Silent (zero overhead) for all other commands.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eSee \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ehooks/HOOK.md\u003c/code\u003e for details.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eReferences\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eDetailed documentation for deeper dives:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Document | Contents |\n| -------- | -------- |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ereferences/data-files.md\u003c/code\u003e | Schema and lineage for every JSON artifact β€” which script writes it, which scripts read it, full data flow diagram. |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003ereferences/language-hints-guide.md\u003c/code\u003e | How to create language hint files, full schema, examples for Hindi/Japanese/Spanish, tips for getting classification right. |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eDesign Principles\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. \u003cstrong style=\"color:#e5e7eb\"\u003eConfig-driven.\u003c/strong\u003e One \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003econfig.py\u003c/code\u003e module, one \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e file. No hardcoded paths anywhere.\n2. \u003cstrong style=\"color:#e5e7eb\"\u003eLanguage-agnostic.\u003c/strong\u003e No baked-in language lists. \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003elangdetect\u003c/code\u003e + optional hint files handle any language.\n3. \u003cstrong style=\"color:#e5e7eb\"\u003ePhased and incremental.\u003c/strong\u003e Each phase builds on the previous. You can stop after any phase and have a useful result.\n4. \u003cstrong style=\"color:#e5e7eb\"\u003eAuditable.\u003c/strong\u003e Every move, merge, and enrichment decision is logged to JSON in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e\u003cDATA_DIR\u003e\u003c/code\u003e.\n5. \u003cstrong style=\"color:#e5e7eb\"\u003eZero-config happy path.\u003c/strong\u003e Clone into your music folder, create a \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e with \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eMUSIC_ROOT\u003c/code\u003e, and run.\n\u003c/p\u003e"])</script><script>self.__next_f.push([1,"20:[\"$\",\"div\",null,{\"className\":\"skill-page\",\"children\":[[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"{\\\"@context\\\":\\\"https://schema.org\\\",\\\"@type\\\":\\\"SoftwareApplication\\\",\\\"name\\\":\\\"Ultimate Music Manager\\\",\\\"description\\\":\\\"Organises a messy local music library into a clean Language/Artist/Album hierarchy using acoustic fingerprinting, deduplication, metadata enrichment, and opt...\\\",\\\"url\\\":\\\"https://bytesagain.com/skill/ultimate-music-manager\\\",\\\"applicationCategory\\\":\\\"requires-oauth-token\\\",\\\"operatingSystem\\\":\\\"Any\\\",\\\"offers\\\":{\\\"@type\\\":\\\"Offer\\\",\\\"price\\\":\\\"0\\\",\\\"priceCurrency\\\":\\\"USD\\\"},\\\"publisher\\\":{\\\"@type\\\":\\\"Organization\\\",\\\"name\\\":\\\"BytesAgain\\\",\\\"url\\\":\\\"https://bytesagain.com\\\"}}\"}}],[\"$\",\"div\",null,{\"className\":\"breadcrumb\",\"children\":[[\"$\",\"a\",null,{\"href\":\"/\",\"children\":\"BytesAgain\"}],\" β€Ί \",[\"$\",\"a\",null,{\"href\":\"/skills\",\"children\":\"Skills\"}],\" β€Ί \",\"Ultimate Music Manager\"]}],[\"$\",\"div\",null,{\"className\":\"two-col\",\"children\":[[\"$\",\"div\",null,{\"className\":\"two-col-main\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-card\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-header\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-badges\",\"children\":[[\"$\",\"span\",null,{\"className\":\"badge\",\"style\":{\"color\":\"#818cf8\",\"background\":\"#818cf822\",\"borderColor\":\"#818cf844\"},\"children\":[\"πŸ¦€\",\" \",\"ClawHub\"]}],false]}],[\"$\",\"div\",null,{\"className\":\"skill-top-actions\",\"children\":[\"$\",\"$L22\",null,{\"slug\":\"ultimate-music-manager\"}]}]]}],[\"$\",\"h1\",null,{\"className\":\"skill-title\",\"children\":\"Ultimate Music Manager\"}],[\"$\",\"p\",null,{\"className\":\"skill-owner\",\"children\":[\"by \",[\"$\",\"span\",null,{\"children\":[\"@\",\"drajb\"]}]]}],[\"$\",\"p\",null,{\"className\":\"skill-desc\",\"children\":\"Organises a messy local music library into a clean Language/Artist/Album hierarchy using acoustic fingerprinting, deduplication, metadata enrichment, and opt...\"}],[\"$\",\"div\",null,{\"className\":\"skill-meta\",\"children\":[[\"$\",\"div\",null,{\"className\":\"meta-item\",\"children\":[[\"$\",\"span\",null,{\"className\":\"meta-label\",\"children\":\"Version\"}],[\"$\",\"span\",null,{\"className\":\"meta-value\",\"children\":[\"v\",\"1.0.1\"]}]]}],[\"$\",\"div\",null,{\"className\":\"meta-item\",\"children\":[[\"$\",\"span\",null,{\"className\":\"meta-label\",\"children\":\"Downloads\"}],[\"$\",\"span\",null,{\"className\":\"meta-value\",\"children\":\"294\"}]]}],false,false,false,[\"$\",\"div\",null,{\"className\":\"meta-item\",\"style\":{\"flexDirection\":\"row\",\"gap\":6,\"alignItems\":\"center\"},\"children\":[[\"$\",\"a\",\"crypto-defi\",{\"href\":\"/?q=crypto-defi\",\"className\":\"tag\",\"children\":[\"#\",\"crypto-defi\"]}],[\"$\",\"a\",\"security\",{\"href\":\"/?q=security\",\"className\":\"tag\",\"children\":[\"#\",\"security\"]}],[\"$\",\"a\",\"audio\",{\"href\":\"/?q=audio\",\"className\":\"tag\",\"children\":[\"#\",\"audio\"]}],[\"$\",\"a\",\"translation\",{\"href\":\"/?q=translation\",\"className\":\"tag\",\"children\":[\"#\",\"translation\"]}],[\"$\",\"a\",\"legal\",{\"href\":\"/?q=legal\",\"className\":\"tag\",\"children\":[\"#\",\"legal\"]}]]}]]}],[\"$\",\"div\",null,{\"style\":{\"marginTop\":6},\"children\":[\"$\",\"a\",null,{\"href\":\"https://clawhub.ai/drajb/ultimate-music-manager\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"btn-secondary\",\"style\":{\"padding\":\"6px 12px\",\"fontSize\":\".82em\",\"borderRadius\":8,\"background\":\"transparent\",\"border\":\"1px solid var(--border-card)\",\"color\":\"var(--text-muted2)\",\"textDecoration\":\"none\",\"whiteSpace\":\"nowrap\"},\"children\":[\"View on \",\"ClawHub\",\" β†’\"]}]}]]}],[\"$\",\"div\",null,{\"className\":\"install-box\",\"children\":[[\"$\",\"div\",null,{\"className\":\"install-header\",\"children\":[[\"$\",\"div\",null,{\"className\":\"install-dots\",\"children\":[[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#ef4444\"}}],[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#eab308\"}}],[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#22c55e\"}}]]}],[\"$\",\"span\",null,{\"className\":\"install-label\",\"children\":\"TERMINAL\"}]]}],[\"$\",\"div\",null,{\"className\":\"install-body\",\"style\":{\"flexWrap\":\"wrap\"},\"children\":[[\"$\",\"code\",null,{\"className\":\"install-cmd\",\"children\":\"clawhub install ultimate-music-manager\"}],[\"$\",\"button\",null,{\"className\":\"copy-btn\",\"data-cmd\":\"clawhub install ultimate-music-manager\",\"style\":{\"fontWeight\":700},\"children\":\"Copy\"}]]}]]}],[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"πŸ“– About This Skill\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$23\"}}]]}],null,\"$L24\",\"$L25\",\"$L26\",null,null,null,false,false]}],\"$L27\"]}]]}]\n"])</script><script>self.__next_f.push([1,"21:[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n document.querySelectorAll('.copy-btn, .script-copy-btn').forEach(btn =\u003e {\\n btn.addEventListener('click', () =\u003e {\\n const cmd = btn.getAttribute('data-cmd');\\n if (!cmd) return;\\n navigator.clipboard.writeText(cmd).then(() =\u003e {\\n const orig = btn.textContent;\\n btn.textContent = 'Copied!';\\n setTimeout(() =\u003e btn.textContent = orig, 1500);\\n }).catch(() =\u003e {});\\n });\\n });\\n \"}}]\n"])</script><script>self.__next_f.push([1,"2b:I[71521,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n28:T40f,\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Check environment is ready\nbash ultimate-music-manager/scripts/preflight.sh\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003ePreview what the pipeline will do\u003c/h2\u003e\nbash ultimate-music-manager/scripts/run-pipeline.sh --dry-run\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eRun the full pipeline\u003c/h2\u003e\nbash ultimate-music-manager/scripts/run-pipeline.sh\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eRun including Spotify sync\u003c/h2\u003e\nbash ultimate-music-manager/scripts/run-pipeline.sh --spotify\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eCheck pipeline status at any time\u003c/h2\u003e\nbash ultimate-music-manager/scripts/status.sh\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e24:[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"πŸ’‘ Examples\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$28\"}}]]}]\n29:Tc35,"])</script><script>self.__next_f.push([1,"\u003cp style=\"margin:8px 0\"\u003eCreate a \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e file in the repo root (copy from \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env.example\u003c/code\u003e):\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# REQUIRED β€” absolute path to the root of your music collection\nMUSIC_ROOT=/path/to/your/music\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eOPTIONAL β€” override any of these defaults:\u003c/h2\u003e\n\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eSORTED_ROOT=/path/to/your/music/Sorted\u003c/h2\u003e\n\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eDATA_DIR=/path/to/your/music/.data\u003c/h2\u003e\n\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eSHAZAM_CONCURRENCY=20\u003c/h2\u003e\n\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eITUNES_COUNTRIES=US,GB\u003c/h2\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eVerify configuration:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003epython config.py\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eThis prints all resolved paths and confirms \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eMUSIC_ROOT\u003c/code\u003e exists.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eDirectory Layout After Setup\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e\u003cMUSIC_ROOT\u003e/\nβ”œβ”€β”€ sonic-phoenix/ # This repo\nβ”‚ β”œβ”€β”€ config.py # Central configuration (single source of truth)\nβ”‚ β”œβ”€β”€ .env # Your local overrides (gitignored)\nβ”‚ β”œβ”€β”€ 01A_extract_metadata.py\nβ”‚ β”œβ”€β”€ 01D_shazam_all_files.py\nβ”‚ β”œβ”€β”€ ...\nβ”‚ └── config/\nβ”‚ └── language_hints/ # Optional language classification overrides\nβ”‚ └── examples/ # Templates to copy and customise\nβ”œβ”€β”€ Sorted/ # Pipeline output (auto-created)\nβ”‚ β”œβ”€β”€ English/\nβ”‚ β”‚ β”œβ”€β”€ Adele/\nβ”‚ β”‚ β”‚ └── 25/\nβ”‚ β”‚ β”‚ └── Adele - Hello.mp3\nβ”‚ β”‚ └── ...\nβ”‚ β”œβ”€β”€ Hindi/\nβ”‚ └── \u003cany language langdetect finds\u003e/\nβ”œβ”€β”€ .data/ # Machine-generated JSON artifacts (auto-created)\nβ”‚ β”œβ”€β”€ metadata_catalog.json\nβ”‚ β”œβ”€β”€ shazam_final_results.json\nβ”‚ β”œβ”€β”€ catalog.json\nβ”‚ β”œβ”€β”€ final_catalog.json\nβ”‚ └── ...\n└── \u003cyour unsorted audio files\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e"])</script><script>self.__next_f.push([1,"25:[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"βš™οΈ Configuration\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$29\"}}]]}]\n2a:T8b7,"])</script><script>self.__next_f.push([1,"\u003cp style=\"margin:8px 0\"\u003e| Problem | Fix |\n|---------|-----|\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eshazamio-core\u003c/code\u003e fails to install | You're on Python 3.13+. Use Python 3.12: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003epython3.12 -m venv .venv\u003c/code\u003e |\n| Shazam returns HTTP 429 | Lower \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eSHAZAM_CONCURRENCY\u003c/code\u003e to 5-10 in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e |\n| iTunes returns 403 | Add more country codes: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eITUNES_COUNTRIES=US,GB,AU,CA,DE\u003c/code\u003e |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eMUSIC_ROOT does not exist\u003c/code\u003e | Set \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eMUSIC_ROOT\u003c/code\u003e in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e.env\u003c/code\u003e to the absolute path of your music folder |\n| Tags still show junk after enrichment | Re-run \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e04I_polish_and_enrich_v6.py\u003c/code\u003e β€” it's idempotent |\n| Spotify 403 on playlist creation | Add your Spotify account under \"Users and Access\" in the Developer Dashboard (Development Mode restriction) |\n| \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003elangdetect\u003c/code\u003e misclassifies a language | Create a hint file in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003econfig/language_hints/\u003c/code\u003e for that language |\n| Empty artist/album folders after sorting | Run \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05E_final_cleanup.py\u003c/code\u003e or \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e05H_final_vacuum.py\u003c/code\u003e to vacuum empties |\u003c/p\u003e"])</script><script>self.__next_f.push([1,"26:[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"πŸ“‹ Tips \u0026 Best Practices\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$2a\"}}]]}]\n27:[\"$\",\"div\",null,{\"className\":\"two-col-side\",\"children\":[\"$\",\"$L2b\",null,{\"category\":\"requires-oauth-token\",\"currentSlug\":\"ultimate-music-manager\",\"name\":\"Ultimate Music Manager\",\"tags\":[\"crypto-defi\",\"security\",\"audio\",\"translation\",\"legal\",\"productivity\"]}]}]\n"])</script></body></html>