Rewrite musicfetch as v2: dual-source search with interactive picker
Accept free-form queries (artist/album/title and combos) instead of strict "Artist - Track". Search Lidarr and YouTube Music concurrently, present a unified rich picker with bold keyword matching, and act on the chosen hit. - Normalize results via a Hit dataclass across both sources - Lidarr: /api/v1/search (album+artist) with album/artist lookup fallback - YouTube: ytmusicapi for accurate metadata + music.youtube.com URLs, yt-dlp scrape fallback; tag overrides from the chosen hit - Lidarr album pick adds artist+album monitored, runs interactive release search, and falls through to top YouTube hit when no indexer release exists - argparse CLI: -n/--noninteractive, -s/--ytsearch, -d/--dry-run, -q/--quality, --limit, --lidarr-only/--yt-only, -o/--root, --search-all - Config via LIDARR_URL / LIDARR_API_KEY / MUSICFETCH_ROOT env vars - Update README; add .gitignore for __pycache__ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
147
README.md
147
README.md
@@ -1,132 +1,149 @@
|
||||
# 🎵 MusicFetch
|
||||
|
||||
**MusicFetch** is a smart command-line utility that fetches music by querying Lidarr (a music collection manager) or, if no match is found or a timeout occurs, falls back to downloading using `yt-dlp`. It supports input in the form of either:
|
||||
**MusicFetch** is a smart command-line utility that finds music by searching
|
||||
**Lidarr** (your music collection manager) and **YouTube Music** at the same
|
||||
time, shows you the top hits in an interactive picker, and downloads/queues
|
||||
whatever you choose. It accepts:
|
||||
|
||||
- `"Artist - Track"` (e.g. `"Daft Punk - Harder Better Faster Stronger"`)
|
||||
- A full YouTube URL (e.g. `"https://www.youtube.com/watch?v=dQw4w9WgXcQ"`)
|
||||
- A **free-form query**: an artist, an album, a track title, or combos like
|
||||
`"Artist - Title"` or `"Artist - Album"` (e.g. `"ODESZA - Bloom"`, `"Daft Punk"`, `"Discovery"`).
|
||||
- A **URL** (e.g. `"https://music.youtube.com/watch?v=..."` or a regular YouTube URL).
|
||||
|
||||
The downloaded track is organized into your Lidarr media folder or a YouTube subfolder if it's a fallback.
|
||||
Lidarr is tried first by default. If you pick a Lidarr album but **no indexer
|
||||
release is available**, MusicFetch automatically falls through to the top
|
||||
YouTube hit. YouTube downloads prefer **YouTube Music URLs** so album art and
|
||||
tags are correct.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Features
|
||||
|
||||
- Searches for and adds artists to Lidarr.
|
||||
- Automatically triggers album searches in Lidarr.
|
||||
- Falls back to downloading via `yt-dlp` if no match is found.
|
||||
- Supports metadata extraction from YouTube URLs.
|
||||
- Handles Lidarr timeouts gracefully.
|
||||
- One unified picker showing the top hits from **Lidarr and YouTube together**, with matching keywords **bolded**.
|
||||
- Lidarr-first flow: pick an album → adds artist+album (monitored) → interactive indexer search → falls through to YouTube only if no release is found.
|
||||
- Accurate YouTube metadata via `ytmusicapi` (real artist / album / year / album art), with `yt-dlp` scraping as a fallback.
|
||||
- Explicit tag overrides on download so files are tagged from the chosen hit, not from scraped titles.
|
||||
- Non-interactive, YouTube-first, dry-run, quality, limit, and source-restriction flags.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Dependencies & Installation
|
||||
|
||||
### 🐍 Python Dependencies
|
||||
### 🐍 Python
|
||||
|
||||
- Python 3.6+
|
||||
- Python 3.10+
|
||||
- `requests`
|
||||
|
||||
Install Python dependencies:
|
||||
- `ytmusicapi` (recommended — accurate YouTube Music metadata)
|
||||
- `rich` (recommended — nicer picker table + bold keyword matching)
|
||||
|
||||
```bash
|
||||
pip install requests
|
||||
pip install requests ytmusicapi rich
|
||||
```
|
||||
|
||||
> **Note:** if you hit `ModuleNotFoundError: No module named 'idna'` from
|
||||
> `requests`, repair it with:
|
||||
> ```bash
|
||||
> pip install --force-reinstall idna requests
|
||||
> ```
|
||||
|
||||
`ytmusicapi` and `rich` are optional — without them MusicFetch falls back to
|
||||
`yt-dlp` search scraping and a plain ANSI picker.
|
||||
|
||||
### 📼 External Tools
|
||||
|
||||
- `yt-dlp`: YouTube downloader for audio/video.
|
||||
|
||||
Install via pip (recommended):
|
||||
- `yt-dlp` (audio download/extraction) and `ffmpeg` (for `-x` extraction / embedding).
|
||||
|
||||
```bash
|
||||
pip install -U yt-dlp
|
||||
```
|
||||
|
||||
Or install system-wide (Debian/Ubuntu):
|
||||
|
||||
```bash
|
||||
sudo apt install yt-dlp
|
||||
sudo apt install ffmpeg # or your distro's equivalent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
### Lidarr Setup
|
||||
Set via environment variables:
|
||||
|
||||
- Ensure Lidarr is running and accessible (e.g., [http://localhost:8686](http://localhost:8686)).
|
||||
- Create or retrieve your API key from Lidarr's settings.
|
||||
|
||||
Set your API key in the terminal session:
|
||||
| Variable | Default | Purpose |
|
||||
|-------------------|-------------------------|----------------------------------|
|
||||
| `LIDARR_API_KEY` | *(required for Lidarr)* | Lidarr API key. |
|
||||
| `LIDARR_URL` | `http://localhost:8686` | Lidarr base URL. |
|
||||
| `MUSICFETCH_ROOT` | `/media/music` | Default output root folder. |
|
||||
|
||||
```bash
|
||||
export LIDARR_API_KEY="your-lidarr-api-key"
|
||||
```
|
||||
|
||||
Or run inline:
|
||||
|
||||
```bash
|
||||
LIDARR_API_KEY="your-lidarr-api-key" ./musicfetch "Artist - Track"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧑💻 Usage
|
||||
|
||||
### 🔉 Download by Search Term
|
||||
|
||||
```bash
|
||||
./musicfetch "Artist - Track"
|
||||
./musicfetch [OPTIONS] QUERY...
|
||||
```
|
||||
|
||||
Example:
|
||||
### Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-n`, `--noninteractive` | Auto-pick the top hit (no prompt). |
|
||||
| `-s`, `--ytsearch` | Search/select YouTube first instead of Lidarr first. |
|
||||
| `-d`, `--dry-run` | Show every action without executing it. |
|
||||
| `-q`, `--quality {best,320,m4a,opus,flac}` | Audio quality/format (default `best`). |
|
||||
| `--limit N` | Hits per source (default 10). |
|
||||
| `--lidarr-only` | Skip YouTube. |
|
||||
| `--yt-only` | Skip Lidarr. |
|
||||
| `-o`, `--root PATH` | Output root folder (default `/media/music`). |
|
||||
| `--search-all` | Search all albums when adding an artist to Lidarr. |
|
||||
| `--debug` | Verbose output. |
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Interactive: combined Lidarr + YouTube picker
|
||||
./musicfetch "ODESZA - Bloom"
|
||||
|
||||
# Just an artist / just an album / just a title all work
|
||||
./musicfetch "Daft Punk"
|
||||
./musicfetch "Discovery"
|
||||
|
||||
# YouTube first, auto-pick top hit
|
||||
./musicfetch -s -n "Daft Punk - Harder Better Faster Stronger"
|
||||
|
||||
# Dry run — see what would happen, change nothing
|
||||
./musicfetch -d "ODESZA - Bloom"
|
||||
|
||||
# YouTube only, lossless preferred
|
||||
./musicfetch --yt-only -q flac "Bonobo - Kerala"
|
||||
|
||||
# Download by URL (YouTube Music URL preferred for correct art)
|
||||
./musicfetch "https://music.youtube.com/watch?v=xxxxxxxxxxx"
|
||||
```
|
||||
|
||||
If Lidarr finds the artist and album, it will trigger a download in Lidarr. If not, the song will be downloaded via yt-dlp using a YouTube search.
|
||||
|
||||
### 📺 Download by URL
|
||||
|
||||
```bash
|
||||
./musicfetch "https://www.youtube.com/watch?v=xxxxxxxxxxx"
|
||||
```
|
||||
|
||||
The script extracts metadata from the video and organizes the file under the artist's folder.
|
||||
|
||||
### 📁 Output Structure
|
||||
|
||||
Music is saved in:
|
||||
|
||||
```text
|
||||
/base_dir/
|
||||
<root>/
|
||||
├── Artist Name/
|
||||
│ ├── Album Name/ (if found via Lidarr)
|
||||
│ └── youtube/ (if fallback used)
|
||||
│ ├── Album Name/ (managed by Lidarr)
|
||||
│ └── youtube/ (yt-dlp downloads / fallbacks)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ❓ Troubleshooting
|
||||
|
||||
- **No results from Lidarr:**
|
||||
- Check to make sure your Lidarr installation is reachable and the artist exists or can be found in Lidarr's metadata sources.
|
||||
- **yt-dlp errors:**
|
||||
- Try updating it:
|
||||
|
||||
```bash
|
||||
yt-dlp -U
|
||||
```
|
||||
|
||||
- **Permission denied or file not found?**
|
||||
- Ensure `/media/music` exists and is writable.
|
||||
- **No Lidarr hits / "LIDARR_API_KEY not set":** export your key and confirm `LIDARR_URL` is reachable.
|
||||
- **Wrong album art from YouTube:** install `ytmusicapi` so MusicFetch can resolve proper YouTube Music URLs and metadata.
|
||||
- **`yt-dlp` errors:** update with `yt-dlp -U`; ensure `ffmpeg` is installed for extraction/embedding.
|
||||
- **`idna` import error:** `pip install --force-reinstall idna requests`.
|
||||
- **Permission denied writing files:** ensure the output root exists and is writable (`-o`/`--root` or `MUSICFETCH_ROOT`).
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Contributing
|
||||
|
||||
PRs are welcome! Please make your changes easy to follow. This script is designed to act as middleware, not a replacement or plugin for Lidarr. Changes must also be compatible with Bash-based workflows.
|
||||
PRs welcome. This script is middleware around Lidarr + yt-dlp, not a Lidarr
|
||||
replacement. Keep it a single bash-friendly executable.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user