Metadata-Version: 2.4
Name: slskify
Version: 0.1.0
Summary: Download music through slskd using Spotify URLs
License: MIT License
        
        Copyright (c) 2026 slskify contributors
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: downloader,music,slskd,soulseek,spotify
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Sound/Audio
Requires-Python: >=3.12
Requires-Dist: click>=8.1
Requires-Dist: httpx>=0.27
Requires-Dist: python-dotenv>=1.0
Requires-Dist: rapidfuzz>=3.6
Requires-Dist: spotipy>=2.23
Provides-Extra: dev
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# slskify

Download music from **Spotify URLs** directly through a running [slskd](https://github.com/slsknet/slskd) instance (Soulseek daemon).

Give it a Spotify track, album, playlist, or artist link — slskify fetches the track list, searches the Soulseek network, scores every result, and queues the best matches for download.

---

## Features

- **All Spotify resource types** — tracks, albums, playlists, artists
- **Smart candidate scoring** — ranks results by title match, album match, track number, artist, codec, and quality; penalises version mismatches (live, remix, acoustic, etc.)
- **Album cohesion** — for album downloads, prefers files from a single consistent rip/source
- **Concurrent searches** — configurable worker pool for fast playlist downloads
- **Flexible codec/bitrate preferences** — prefer FLAC, 320 kbps MP3, etc.
- **Zero credentials in shell history** — all secrets are read from environment variables or a `.env` file

---

## Requirements

- Python 3.12+
- A running [slskd](https://github.com/slsknet/slskd) instance with API access enabled
- A [Spotify app](https://developer.spotify.com/dashboard) (free) for the Client Credentials flow

---

## Installation

```bash
pip install slskify
```

Or install from source:

```bash
git clone https://github.com/cmerk2021/slskify.git
cd slskify
pip install -e .
```

---

## Configuration

slskify reads credentials from environment variables or a `.env` file in the working directory.

Copy the example and fill in your values:

```bash
cp .env.example .env
```

```dotenv
# Spotify — https://developer.spotify.com/dashboard
SPOTIFY_CLIENT_ID=your_spotify_client_id
SPOTIFY_CLIENT_SECRET=your_spotify_client_secret

# slskd instance
SLSKD_URL=http://localhost:5030
SLSKD_API_KEY=your_slskd_api_key
```

All four values can alternatively be passed as CLI flags (see `slskify --help`).

---

## Usage

```
slskify [OPTIONS] URL
```

`URL` can be any Spotify track, album, playlist, or artist link.

### Options

| Flag | Default | Description |
|---|---|---|
| `--codec CODEC` | — | Preferred codec (`flac`, `mp3`, `aac`, …). Other codecs score 0. |
| `--bitrate KBPS` | — | Preferred bitrate in kbps (e.g. `320`). Used to rank lossy files. |
| `--slskd-url URL` | `SLSKD_URL` env | Base URL of the slskd instance. |
| `--slskd-api-key KEY` | `SLSKD_API_KEY` env | slskd API key. |
| `--spotify-client-id ID` | `SPOTIFY_CLIENT_ID` env | Spotify app client ID. |
| `--spotify-client-secret SECRET` | `SPOTIFY_CLIENT_SECRET` env | Spotify app client secret. |
| `--search-timeout SECONDS` | `30` | Per-query search timeout sent to slskd. |
| `--workers N` | `4` | Max concurrent searches for multi-track downloads. |
| `-v, --verbose` | off | Log every HTTP request and internal scoring decision. |
| `-V, --version` | — | Show version and exit. |
| `-h, --help` | — | Show help and exit. |

### Examples

```bash
# Download an album, prefer FLAC
slskify https://open.spotify.com/album/4LH4d3cOWNNsVw41Gqt2kv --codec flac

# Download a playlist, prefer 320 kbps MP3
slskify https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M --codec mp3 --bitrate 320

# Download a single track
slskify https://open.spotify.com/track/4cOdK2wGLETKBW3PvgPWqT

# Download all tracks by an artist
slskify https://open.spotify.com/artist/06HL4z0CvFAxyc27GXpf02

# Show version
slskify --version
```

---

## How it works

1. **Resolve** — the Spotify URL is parsed and the full track list is fetched via the Spotify Web API (Client Credentials; no login needed).
2. **Search** — for each track, several queries of varying specificity are sent to slskd (e.g. `"Artist Title"`, `"Artist Album"`, `"Title"`). Results are deduplicated.
3. **Score** — each audio file candidate is scored across six weighted components: title match, album match, track number, artist match, codec preference, and audio quality. Version mismatches (live, remix, acoustic, etc.) incur a large penalty.
4. **Album cohesion** — for album downloads, a single user+folder source that covers the most tracks is identified and preferred, so all files come from one consistent rip.
5. **Queue** — the best candidate above the minimum score threshold is queued for download via slskd's REST API.

---

## Development

```bash
# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run tests with coverage
pytest --cov=slskify --cov-report=term-missing
```

---

## Publishing a new release

Releases are published to PyPI automatically via GitHub Actions when you create a GitHub Release.

### One-time setup (PyPI Trusted Publishing)

1. Go to [https://pypi.org](https://pypi.org) and create an account / log in.
2. On PyPI → Your project → **Publishing** → add a **Trusted Publisher**:
   - Publisher: **GitHub Actions**
   - Repository owner: `YOUR_USERNAME`
   - Repository name: `slskify`
   - Workflow name: `publish.yml`
   - Environment name: `pypi`
3. In your GitHub repo → **Settings** → **Environments** → create an environment named `pypi`.

### Releasing

1. Bump the version in `slskify/version.py` and `pyproject.toml`.
2. Commit and push.
3. On GitHub, go to **Releases** → **Draft a new release**.
4. Create a new tag (e.g. `v0.2.0`), write release notes, and click **Publish release**.
5. The Actions workflow builds the wheel + sdist and uploads to PyPI automatically.

### First manual release (before Trusted Publishing is set up)

```bash
pip install build twine
python -m build
twine upload dist/*
```

---

## License

[MIT](LICENSE)
