trackfetch 2.0.0__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {trackfetch-2.0.0 → trackfetch-2.2.0}/PKG-INFO +125 -33
- {trackfetch-2.0.0 → trackfetch-2.2.0}/README.md +124 -32
- {trackfetch-2.0.0 → trackfetch-2.2.0}/pyproject.toml +1 -1
- trackfetch-2.2.0/tests/test_config.py +346 -0
- {trackfetch-2.0.0 → trackfetch-2.2.0}/tests/test_pipeline.py +56 -7
- {trackfetch-2.0.0 → trackfetch-2.2.0}/tests/test_tags_and_files.py +105 -0
- {trackfetch-2.0.0 → trackfetch-2.2.0}/tests/test_text.py +5 -0
- {trackfetch-2.0.0 → trackfetch-2.2.0}/tests/test_youtube.py +30 -0
- {trackfetch-2.0.0 → trackfetch-2.2.0}/trackfetch.egg-info/PKG-INFO +125 -33
- {trackfetch-2.0.0 → trackfetch-2.2.0}/trackfetch.egg-info/SOURCES.txt +1 -0
- {trackfetch-2.0.0 → trackfetch-2.2.0}/trackfetch.py +394 -17
- {trackfetch-2.0.0 → trackfetch-2.2.0}/LICENSE +0 -0
- {trackfetch-2.0.0 → trackfetch-2.2.0}/setup.cfg +0 -0
- {trackfetch-2.0.0 → trackfetch-2.2.0}/tests/test_spotify.py +0 -0
- {trackfetch-2.0.0 → trackfetch-2.2.0}/trackfetch.egg-info/dependency_links.txt +0 -0
- {trackfetch-2.0.0 → trackfetch-2.2.0}/trackfetch.egg-info/entry_points.txt +0 -0
- {trackfetch-2.0.0 → trackfetch-2.2.0}/trackfetch.egg-info/requires.txt +0 -0
- {trackfetch-2.0.0 → trackfetch-2.2.0}/trackfetch.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trackfetch
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Turn a text file of songs into tagged MP3s: Spotify metadata and cover art, best-match YouTube audio via yt-dlp.
|
|
5
5
|
Author: ByteMe6
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -30,20 +30,21 @@ Requires-Dist: pytest-cov>=5; extra == "test"
|
|
|
30
30
|
Dynamic: license-file
|
|
31
31
|
|
|
32
32
|
<picture>
|
|
33
|
-
<source media="(prefers-color-scheme: dark)" srcset=".github/assets/banner-dark.svg">
|
|
34
|
-
<img alt="trackfetch: a text file of songs in, tagged MP3s with cover art out" src=".github/assets/banner-light.svg" width="100%">
|
|
33
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/ByteMe6/trackfetch/master/.github/assets/banner-dark.svg">
|
|
34
|
+
<img alt="trackfetch: a text file of songs in, tagged MP3s with cover art out" src="https://raw.githubusercontent.com/ByteMe6/trackfetch/master/.github/assets/banner-light.svg" width="100%">
|
|
35
35
|
</picture>
|
|
36
36
|
|
|
37
37
|
<p align="center">
|
|
38
38
|
<a href="https://github.com/ByteMe6/trackfetch/releases/latest"><img alt="Latest release" src="https://img.shields.io/github/v/release/ByteMe6/trackfetch?style=flat-square&label=release&labelColor=15122B&color=FFB547"></a>
|
|
39
|
+
<a href="https://pypi.org/project/trackfetch/"><img alt="PyPI" src="https://img.shields.io/pypi/v/trackfetch?style=flat-square&label=pypi&labelColor=15122B&color=FFB547"></a>
|
|
39
40
|
<a href="https://github.com/ByteMe6/trackfetch/actions/workflows/release.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/ByteMe6/trackfetch/release.yml?branch=master&style=flat-square&label=build&labelColor=15122B"></a>
|
|
40
41
|
<a href="#install"><img alt="Python 3.10+" src="https://img.shields.io/badge/python-3.10%2B-FFB547?style=flat-square&labelColor=15122B"></a>
|
|
41
42
|
<a href="#standalone-binary"><img alt="Linux, macOS and Windows on x86_64 and ARM64" src="https://img.shields.io/badge/linux%20%C2%B7%20macos%20%C2%B7%20windows-x86__64%20%2B%20arm64-FFB547?style=flat-square&labelColor=15122B"></a>
|
|
42
|
-
<a href="LICENSE"><img alt="License: GPL-3.0-or-later" src="https://img.shields.io/badge/license-GPL--3.0--or--later-FFB547?style=flat-square&labelColor=15122B"></a>
|
|
43
|
+
<a href="https://github.com/ByteMe6/trackfetch/blob/master/LICENSE"><img alt="License: GPL-3.0-or-later" src="https://img.shields.io/badge/license-GPL--3.0--or--later-FFB547?style=flat-square&labelColor=15122B"></a>
|
|
43
44
|
</p>
|
|
44
45
|
|
|
45
46
|
<p align="center">
|
|
46
|
-
<b>English</b> · <a href="README.uk.md">Українська</a> · <a href="README.ru.md">Русский</a>
|
|
47
|
+
<b>English</b> · <a href="https://github.com/ByteMe6/trackfetch/blob/master/README.uk.md">Українська</a> · <a href="https://github.com/ByteMe6/trackfetch/blob/master/README.ru.md">Русский</a>
|
|
47
48
|
</p>
|
|
48
49
|
|
|
49
50
|
<p align="center">
|
|
@@ -57,7 +58,7 @@ Dynamic: license-file
|
|
|
57
58
|
|
|
58
59
|
<br>
|
|
59
60
|
|
|
60
|
-
<img alt="Terminal recording: trackfetch skips two songs that are already downloaded, finds Daft Punk - One More Time on Spotify, scores five YouTube results, downloads the best one and saves a tagged MP3" src=".github/assets/demo.svg" width="100%">
|
|
61
|
+
<img alt="Terminal recording: trackfetch skips two songs that are already downloaded, finds Daft Punk - One More Time on Spotify, scores five YouTube results, downloads the best one and saves a tagged MP3" src="https://raw.githubusercontent.com/ByteMe6/trackfetch/master/.github/assets/demo.svg" width="100%">
|
|
61
62
|
|
|
62
63
|
<br>
|
|
63
64
|
|
|
@@ -66,7 +67,7 @@ trackfetch reads a plain text file with one `Artist - Title` per line. For every
|
|
|
66
67
|
- **Exact metadata.** The title, every credited artist, album, album artist, track number and release date come from Spotify, not from a YouTube video title.
|
|
67
68
|
- **Album art.** The largest Spotify cover is embedded in every file.
|
|
68
69
|
- **The right upload.** trackfetch scores five YouTube results per song. Official audio wins; covers, karaoke, nightcore, sped-up, slowed, remix and reaction videos lose.
|
|
69
|
-
- **
|
|
70
|
+
- **Your choice of format.** MP3 for any player, or Opus and M4A that keep YouTube's original audio without re-encoding.
|
|
70
71
|
- **Resumable.** Finished songs are logged in `done.txt` and skipped next time. Failures go to `failed.txt` with the reason.
|
|
71
72
|
- **Plays everywhere.** Tags are written as ID3v2.3, which Windows Explorer, Apple Music, Android, car stereos and most other players read.
|
|
72
73
|
- **Any alphabet.** Cyrillic and other non-Latin titles are matched correctly and kept in filenames. Characters that are illegal in filenames are replaced.
|
|
@@ -74,13 +75,32 @@ trackfetch reads a plain text file with one `Artist - Title` per line. For every
|
|
|
74
75
|
|
|
75
76
|
## Install
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
Pick one. Homebrew and Nix also install yt-dlp, FFmpeg and Deno for you; with the other methods, install them yourself ([see below](#dependencies)). Every method needs a free Spotify API key ([set it up below](#spotify-credentials)).
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
### Homebrew
|
|
81
|
+
|
|
82
|
+
macOS and Linux:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
brew install ByteMe6/tap/trackfetch
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
On Intel Macs, Homebrew builds the dependencies from source, so the first install takes a while.
|
|
89
|
+
|
|
90
|
+
### Nix
|
|
91
|
+
|
|
92
|
+
Linux and Apple Silicon Macs, with flakes enabled:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
nix run github:ByteMe6/trackfetch -- songs.txt # run without installing
|
|
96
|
+
nix profile install github:ByteMe6/trackfetch # install
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### pipx
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pipx install trackfetch
|
|
103
|
+
```
|
|
84
104
|
|
|
85
105
|
### Standalone binary
|
|
86
106
|
|
|
@@ -101,12 +121,6 @@ sudo mv trackfetch-linux-x86_64 /usr/local/bin/trackfetch
|
|
|
101
121
|
|
|
102
122
|
On macOS, if Gatekeeper blocks the unsigned binary, run `xattr -d com.apple.quarantine trackfetch-macos-*` once. Checksums are in `SHA256SUMS.txt` on each release.
|
|
103
123
|
|
|
104
|
-
### pipx
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
pipx install git+https://github.com/ByteMe6/trackfetch.git
|
|
108
|
-
```
|
|
109
|
-
|
|
110
124
|
### From source
|
|
111
125
|
|
|
112
126
|
```bash
|
|
@@ -116,6 +130,16 @@ python -m venv .venv && source .venv/bin/activate
|
|
|
116
130
|
pip install -e .
|
|
117
131
|
```
|
|
118
132
|
|
|
133
|
+
### Dependencies
|
|
134
|
+
|
|
135
|
+
With pipx, a standalone binary or a source install, put these on your `PATH`:
|
|
136
|
+
|
|
137
|
+
| Tool | Used for | Install |
|
|
138
|
+
| --- | --- | --- |
|
|
139
|
+
| [yt-dlp](https://github.com/yt-dlp/yt-dlp) | Searching and downloading from YouTube | `pipx install yt-dlp` · `brew install yt-dlp` · `winget install yt-dlp` |
|
|
140
|
+
| [FFmpeg](https://ffmpeg.org/) | Converting the audio to MP3 | `sudo apt install ffmpeg` · `brew install ffmpeg` · `winget install ffmpeg` |
|
|
141
|
+
| [Deno](https://deno.com/) | JavaScript runtime that yt-dlp needs for YouTube | `curl -fsSL https://deno.land/install.sh \| sh` · `brew install deno` · `winget install DenoLand.Deno` |
|
|
142
|
+
|
|
119
143
|
## Spotify credentials
|
|
120
144
|
|
|
121
145
|
trackfetch uses Spotify's Client Credentials flow: no Spotify login, no access to your account.
|
|
@@ -171,7 +195,7 @@ EOF
|
|
|
171
195
|
trackfetch songs.txt
|
|
172
196
|
```
|
|
173
197
|
|
|
174
|
-
The files land in
|
|
198
|
+
The files land in the current directory.
|
|
175
199
|
|
|
176
200
|
## Playlists from streaming services
|
|
177
201
|
|
|
@@ -189,22 +213,90 @@ trackfetch "My Playlist.txt" -o ~/Music/"My Playlist"
|
|
|
189
213
|
## Usage
|
|
190
214
|
|
|
191
215
|
```text
|
|
192
|
-
trackfetch [-h] [-o OUTPUT] [--title-only] [--delay DELAY] input
|
|
216
|
+
trackfetch [-h] [-o OUTPUT] [--config CONFIG] [-f {mp3,m4a,opus}] [--title-only] [--delay DELAY] [--spotify-client-id ID] [--spotify-client-secret SECRET] [input]
|
|
193
217
|
```
|
|
194
218
|
|
|
195
219
|
| Option | Default | What it does |
|
|
196
220
|
| --- | --- | --- |
|
|
197
|
-
| `input` | required | Text file with one `Artist - Title` per line |
|
|
198
|
-
| `-o`, `--output` |
|
|
199
|
-
| `--
|
|
221
|
+
| `input` | required | Text file with one `Artist - Title` per line. Can also be set in the [config file](#configuration-file). |
|
|
222
|
+
| `-o`, `--output` | current directory | Folder to save the files in. It's created if it doesn't exist. |
|
|
223
|
+
| `--config` | auto-detected | Path to a [config file](#configuration-file). |
|
|
224
|
+
| `-f`, `--format` | `m4a` | Audio format: `mp3`, `m4a` or `opus`. See [Audio formats](#audio-formats). |
|
|
225
|
+
| `--title-only` | off | Name files `Title.m4a` instead of `Artist - Title.m4a` |
|
|
200
226
|
| `--delay` | `1.0` | Seconds to wait between songs. Raise it for long lists. |
|
|
227
|
+
| `--spotify-client-id` | `$SPOTIFY_CLIENT_ID` | Spotify API client ID. See [Spotify credentials](#spotify-credentials). |
|
|
228
|
+
| `--spotify-client-secret` | `$SPOTIFY_CLIENT_SECRET` | Spotify API client secret. |
|
|
201
229
|
|
|
202
230
|
```bash
|
|
203
231
|
trackfetch songs.txt -o ~/Music/RoadTrip # save to a specific folder
|
|
204
232
|
trackfetch songs.txt -o /media/usb --title-only # short names for a car stereo
|
|
205
233
|
trackfetch big-list.txt --delay 3 # go easy on rate limits
|
|
234
|
+
trackfetch songs.txt --format opus # original YouTube audio, no re-encode
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Configuration file
|
|
238
|
+
|
|
239
|
+
Put your defaults in an INI file and forget the flags:
|
|
240
|
+
|
|
241
|
+
```ini
|
|
242
|
+
[trackfetch]
|
|
243
|
+
output = ~/Music/trackfetch
|
|
244
|
+
format = m4a
|
|
245
|
+
title_only = false
|
|
246
|
+
delay = 1.0
|
|
247
|
+
input = ~/songs.txt
|
|
248
|
+
spotify_client_id = your-client-id
|
|
249
|
+
spotify_client_secret = your-client-secret
|
|
206
250
|
```
|
|
207
251
|
|
|
252
|
+
Every option above can be set there, under the `[trackfetch]` section. The
|
|
253
|
+
precedence is always the same:
|
|
254
|
+
|
|
255
|
+
1. the command-line flag,
|
|
256
|
+
2. the config file,
|
|
257
|
+
3. the built-in default (the current directory for the output folder).
|
|
258
|
+
|
|
259
|
+
trackfetch looks for the config file in this order: the path given with
|
|
260
|
+
`--config`, the path in `$TRACKFETCH_CONFIG`, `trackfetch.ini` in the current
|
|
261
|
+
directory, `$XDG_CONFIG_HOME/trackfetch/trackfetch.ini` (usually
|
|
262
|
+
`~/.config/trackfetch/trackfetch.ini`) and `~/.trackfetch.ini`.
|
|
263
|
+
|
|
264
|
+
### Spotify credentials
|
|
265
|
+
|
|
266
|
+
trackfetch needs a Spotify client ID and secret to read the metadata. Give
|
|
267
|
+
them in whichever way suits you:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
# in the config file
|
|
271
|
+
[trackfetch]
|
|
272
|
+
spotify_client_id = your-client-id
|
|
273
|
+
spotify_client_secret = your-client-secret
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
# in the environment
|
|
278
|
+
export SPOTIFY_CLIENT_ID='your-client-id'
|
|
279
|
+
export SPOTIFY_CLIENT_SECRET='your-client-secret'
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
# on the command line
|
|
284
|
+
trackfetch songs.txt --spotify-client-id your-id --spotify-client-secret your-secret
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
The flag wins over the config file, and the config file wins over the
|
|
288
|
+
environment variable.
|
|
289
|
+
|
|
290
|
+
### Audio formats
|
|
291
|
+
|
|
292
|
+
| Format | What you get | Plays on |
|
|
293
|
+
| --- | --- | --- |
|
|
294
|
+
| `m4a` (default) | YouTube's **original** AAC audio when available, copied without re-encoding | Apple devices, iTunes, most players |
|
|
295
|
+
| `opus` | YouTube's **original** Opus audio, copied without re-encoding. The best quality you can get | most modern players, Android, browsers; not iTunes or older devices |
|
|
296
|
+
| `mp3` | YouTube's audio **re-encoded** to the best VBR MP3. A second lossy encode, so slightly below the source | everything, including old players and car stereos |
|
|
297
|
+
|
|
298
|
+
All three get the same tags and embedded cover art. `done.txt` doesn't track the format, so use a separate `-o` folder for each format.
|
|
299
|
+
|
|
208
300
|
### Input file
|
|
209
301
|
|
|
210
302
|
```text
|
|
@@ -213,14 +305,14 @@ Daft Punk - One More Time
|
|
|
213
305
|
Sufjan Stevens - Mystery of Love - Remastered ← only the first " - " separates artist and title
|
|
214
306
|
```
|
|
215
307
|
|
|
216
|
-
The separator is space, hyphen, space (` - `). Lines without it are skipped. See [`examples/songs.txt`](examples/songs.txt).
|
|
308
|
+
The separator is space, hyphen, space (` - `). Lines without it are skipped. See [`examples/songs.txt`](https://github.com/ByteMe6/trackfetch/blob/master/examples/songs.txt).
|
|
217
309
|
|
|
218
310
|
### Output folder
|
|
219
311
|
|
|
220
312
|
```text
|
|
221
|
-
|
|
222
|
-
├── Daft Punk - One More Time.
|
|
223
|
-
├── Radiohead - Paranoid Android.
|
|
313
|
+
./
|
|
314
|
+
├── Daft Punk - One More Time.m4a
|
|
315
|
+
├── Radiohead - Paranoid Android.m4a
|
|
224
316
|
├── done.txt ← finished songs, skipped on the next run
|
|
225
317
|
└── failed.txt ← "Artist - Title | reason" for each song that failed
|
|
226
318
|
```
|
|
@@ -228,7 +320,7 @@ The separator is space, hyphen, space (` - `). Lines without it are skipped. See
|
|
|
228
320
|
**Retry failures** by stripping the reasons from `failed.txt` and running it again:
|
|
229
321
|
|
|
230
322
|
```bash
|
|
231
|
-
cut -d '|' -f 1
|
|
323
|
+
cut -d '|' -f 1 ./failed.txt > retry.txt
|
|
232
324
|
trackfetch retry.txt
|
|
233
325
|
```
|
|
234
326
|
|
|
@@ -236,7 +328,7 @@ trackfetch retry.txt
|
|
|
236
328
|
|
|
237
329
|
### Cheat sheet
|
|
238
330
|
|
|
239
|
-
A [tldr](https://tldr.sh/) page is in [`docs/tldr/trackfetch.md`](docs/tldr/trackfetch.md). To use it with [tealdeer](https://github.com/tealdeer-rs/tealdeer), copy it into your custom pages folder as `trackfetch.page.md`.
|
|
331
|
+
A [tldr](https://tldr.sh/) page is in [`docs/tldr/trackfetch.md`](https://github.com/ByteMe6/trackfetch/blob/master/docs/tldr/trackfetch.md). To use it with [tealdeer](https://github.com/tealdeer-rs/tealdeer), copy it into your custom pages folder as `trackfetch.page.md`.
|
|
240
332
|
|
|
241
333
|
## How it works
|
|
242
334
|
|
|
@@ -313,11 +405,11 @@ pytest --cov=trackfetch
|
|
|
313
405
|
|
|
314
406
|
The tests stub every network and subprocess call, so they run offline in under a second. To build a standalone binary yourself, run `pip install pyinstaller && pyinstaller trackfetch.spec`; it ends up in `dist/`.
|
|
315
407
|
|
|
316
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
|
|
408
|
+
See [CONTRIBUTING.md](https://github.com/ByteMe6/trackfetch/blob/master/CONTRIBUTING.md) before opening a pull request.
|
|
317
409
|
|
|
318
410
|
### Releasing
|
|
319
411
|
|
|
320
|
-
Every push to `master` runs the tests on Linux, macOS and Windows. When `version` in [`pyproject.toml`](pyproject.toml) has no matching `vX.Y.Z` tag yet, the [Build & Release](.github/workflows/release.yml) workflow also builds all six binaries and publishes a release, using that version's section of [`CHANGELOG.md`](CHANGELOG.md) as the notes.
|
|
412
|
+
Every push to `master` runs the tests on Linux, macOS and Windows. When `version` in [`pyproject.toml`](https://github.com/ByteMe6/trackfetch/blob/master/pyproject.toml) has no matching `vX.Y.Z` tag yet, the [Build & Release](https://github.com/ByteMe6/trackfetch/blob/master/.github/workflows/release.yml) workflow also builds all six binaries and publishes a release, using that version's section of [`CHANGELOG.md`](https://github.com/ByteMe6/trackfetch/blob/master/CHANGELOG.md) as the notes.
|
|
321
413
|
|
|
322
414
|
To release, move the changes under `[Unreleased]` in the changelog to a new `## [X.Y.Z] - YYYY-MM-DD` section, bump `version`, and push.
|
|
323
415
|
|
|
@@ -330,4 +422,4 @@ trackfetch is for personal use with content you have the right to download. You
|
|
|
330
422
|
|
|
331
423
|
## License
|
|
332
424
|
|
|
333
|
-
trackfetch is free software, released under the [GNU General Public License v3.0 or later](LICENSE). You can use, study, change and share it. If you distribute a modified version, it must stay under the same license.
|
|
425
|
+
trackfetch is free software, released under the [GNU General Public License v3.0 or later](https://github.com/ByteMe6/trackfetch/blob/master/LICENSE). You can use, study, change and share it. If you distribute a modified version, it must stay under the same license.
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
<picture>
|
|
2
|
-
<source media="(prefers-color-scheme: dark)" srcset=".github/assets/banner-dark.svg">
|
|
3
|
-
<img alt="trackfetch: a text file of songs in, tagged MP3s with cover art out" src=".github/assets/banner-light.svg" width="100%">
|
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/ByteMe6/trackfetch/master/.github/assets/banner-dark.svg">
|
|
3
|
+
<img alt="trackfetch: a text file of songs in, tagged MP3s with cover art out" src="https://raw.githubusercontent.com/ByteMe6/trackfetch/master/.github/assets/banner-light.svg" width="100%">
|
|
4
4
|
</picture>
|
|
5
5
|
|
|
6
6
|
<p align="center">
|
|
7
7
|
<a href="https://github.com/ByteMe6/trackfetch/releases/latest"><img alt="Latest release" src="https://img.shields.io/github/v/release/ByteMe6/trackfetch?style=flat-square&label=release&labelColor=15122B&color=FFB547"></a>
|
|
8
|
+
<a href="https://pypi.org/project/trackfetch/"><img alt="PyPI" src="https://img.shields.io/pypi/v/trackfetch?style=flat-square&label=pypi&labelColor=15122B&color=FFB547"></a>
|
|
8
9
|
<a href="https://github.com/ByteMe6/trackfetch/actions/workflows/release.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/ByteMe6/trackfetch/release.yml?branch=master&style=flat-square&label=build&labelColor=15122B"></a>
|
|
9
10
|
<a href="#install"><img alt="Python 3.10+" src="https://img.shields.io/badge/python-3.10%2B-FFB547?style=flat-square&labelColor=15122B"></a>
|
|
10
11
|
<a href="#standalone-binary"><img alt="Linux, macOS and Windows on x86_64 and ARM64" src="https://img.shields.io/badge/linux%20%C2%B7%20macos%20%C2%B7%20windows-x86__64%20%2B%20arm64-FFB547?style=flat-square&labelColor=15122B"></a>
|
|
11
|
-
<a href="LICENSE"><img alt="License: GPL-3.0-or-later" src="https://img.shields.io/badge/license-GPL--3.0--or--later-FFB547?style=flat-square&labelColor=15122B"></a>
|
|
12
|
+
<a href="https://github.com/ByteMe6/trackfetch/blob/master/LICENSE"><img alt="License: GPL-3.0-or-later" src="https://img.shields.io/badge/license-GPL--3.0--or--later-FFB547?style=flat-square&labelColor=15122B"></a>
|
|
12
13
|
</p>
|
|
13
14
|
|
|
14
15
|
<p align="center">
|
|
15
|
-
<b>English</b> · <a href="README.uk.md">Українська</a> · <a href="README.ru.md">Русский</a>
|
|
16
|
+
<b>English</b> · <a href="https://github.com/ByteMe6/trackfetch/blob/master/README.uk.md">Українська</a> · <a href="https://github.com/ByteMe6/trackfetch/blob/master/README.ru.md">Русский</a>
|
|
16
17
|
</p>
|
|
17
18
|
|
|
18
19
|
<p align="center">
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
|
|
27
28
|
<br>
|
|
28
29
|
|
|
29
|
-
<img alt="Terminal recording: trackfetch skips two songs that are already downloaded, finds Daft Punk - One More Time on Spotify, scores five YouTube results, downloads the best one and saves a tagged MP3" src=".github/assets/demo.svg" width="100%">
|
|
30
|
+
<img alt="Terminal recording: trackfetch skips two songs that are already downloaded, finds Daft Punk - One More Time on Spotify, scores five YouTube results, downloads the best one and saves a tagged MP3" src="https://raw.githubusercontent.com/ByteMe6/trackfetch/master/.github/assets/demo.svg" width="100%">
|
|
30
31
|
|
|
31
32
|
<br>
|
|
32
33
|
|
|
@@ -35,7 +36,7 @@ trackfetch reads a plain text file with one `Artist - Title` per line. For every
|
|
|
35
36
|
- **Exact metadata.** The title, every credited artist, album, album artist, track number and release date come from Spotify, not from a YouTube video title.
|
|
36
37
|
- **Album art.** The largest Spotify cover is embedded in every file.
|
|
37
38
|
- **The right upload.** trackfetch scores five YouTube results per song. Official audio wins; covers, karaoke, nightcore, sped-up, slowed, remix and reaction videos lose.
|
|
38
|
-
- **
|
|
39
|
+
- **Your choice of format.** MP3 for any player, or Opus and M4A that keep YouTube's original audio without re-encoding.
|
|
39
40
|
- **Resumable.** Finished songs are logged in `done.txt` and skipped next time. Failures go to `failed.txt` with the reason.
|
|
40
41
|
- **Plays everywhere.** Tags are written as ID3v2.3, which Windows Explorer, Apple Music, Android, car stereos and most other players read.
|
|
41
42
|
- **Any alphabet.** Cyrillic and other non-Latin titles are matched correctly and kept in filenames. Characters that are illegal in filenames are replaced.
|
|
@@ -43,13 +44,32 @@ trackfetch reads a plain text file with one `Artist - Title` per line. For every
|
|
|
43
44
|
|
|
44
45
|
## Install
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
Pick one. Homebrew and Nix also install yt-dlp, FFmpeg and Deno for you; with the other methods, install them yourself ([see below](#dependencies)). Every method needs a free Spotify API key ([set it up below](#spotify-credentials)).
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
### Homebrew
|
|
50
|
+
|
|
51
|
+
macOS and Linux:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
brew install ByteMe6/tap/trackfetch
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
On Intel Macs, Homebrew builds the dependencies from source, so the first install takes a while.
|
|
58
|
+
|
|
59
|
+
### Nix
|
|
60
|
+
|
|
61
|
+
Linux and Apple Silicon Macs, with flakes enabled:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
nix run github:ByteMe6/trackfetch -- songs.txt # run without installing
|
|
65
|
+
nix profile install github:ByteMe6/trackfetch # install
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### pipx
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pipx install trackfetch
|
|
72
|
+
```
|
|
53
73
|
|
|
54
74
|
### Standalone binary
|
|
55
75
|
|
|
@@ -70,12 +90,6 @@ sudo mv trackfetch-linux-x86_64 /usr/local/bin/trackfetch
|
|
|
70
90
|
|
|
71
91
|
On macOS, if Gatekeeper blocks the unsigned binary, run `xattr -d com.apple.quarantine trackfetch-macos-*` once. Checksums are in `SHA256SUMS.txt` on each release.
|
|
72
92
|
|
|
73
|
-
### pipx
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
pipx install git+https://github.com/ByteMe6/trackfetch.git
|
|
77
|
-
```
|
|
78
|
-
|
|
79
93
|
### From source
|
|
80
94
|
|
|
81
95
|
```bash
|
|
@@ -85,6 +99,16 @@ python -m venv .venv && source .venv/bin/activate
|
|
|
85
99
|
pip install -e .
|
|
86
100
|
```
|
|
87
101
|
|
|
102
|
+
### Dependencies
|
|
103
|
+
|
|
104
|
+
With pipx, a standalone binary or a source install, put these on your `PATH`:
|
|
105
|
+
|
|
106
|
+
| Tool | Used for | Install |
|
|
107
|
+
| --- | --- | --- |
|
|
108
|
+
| [yt-dlp](https://github.com/yt-dlp/yt-dlp) | Searching and downloading from YouTube | `pipx install yt-dlp` · `brew install yt-dlp` · `winget install yt-dlp` |
|
|
109
|
+
| [FFmpeg](https://ffmpeg.org/) | Converting the audio to MP3 | `sudo apt install ffmpeg` · `brew install ffmpeg` · `winget install ffmpeg` |
|
|
110
|
+
| [Deno](https://deno.com/) | JavaScript runtime that yt-dlp needs for YouTube | `curl -fsSL https://deno.land/install.sh \| sh` · `brew install deno` · `winget install DenoLand.Deno` |
|
|
111
|
+
|
|
88
112
|
## Spotify credentials
|
|
89
113
|
|
|
90
114
|
trackfetch uses Spotify's Client Credentials flow: no Spotify login, no access to your account.
|
|
@@ -140,7 +164,7 @@ EOF
|
|
|
140
164
|
trackfetch songs.txt
|
|
141
165
|
```
|
|
142
166
|
|
|
143
|
-
The files land in
|
|
167
|
+
The files land in the current directory.
|
|
144
168
|
|
|
145
169
|
## Playlists from streaming services
|
|
146
170
|
|
|
@@ -158,22 +182,90 @@ trackfetch "My Playlist.txt" -o ~/Music/"My Playlist"
|
|
|
158
182
|
## Usage
|
|
159
183
|
|
|
160
184
|
```text
|
|
161
|
-
trackfetch [-h] [-o OUTPUT] [--title-only] [--delay DELAY] input
|
|
185
|
+
trackfetch [-h] [-o OUTPUT] [--config CONFIG] [-f {mp3,m4a,opus}] [--title-only] [--delay DELAY] [--spotify-client-id ID] [--spotify-client-secret SECRET] [input]
|
|
162
186
|
```
|
|
163
187
|
|
|
164
188
|
| Option | Default | What it does |
|
|
165
189
|
| --- | --- | --- |
|
|
166
|
-
| `input` | required | Text file with one `Artist - Title` per line |
|
|
167
|
-
| `-o`, `--output` |
|
|
168
|
-
| `--
|
|
190
|
+
| `input` | required | Text file with one `Artist - Title` per line. Can also be set in the [config file](#configuration-file). |
|
|
191
|
+
| `-o`, `--output` | current directory | Folder to save the files in. It's created if it doesn't exist. |
|
|
192
|
+
| `--config` | auto-detected | Path to a [config file](#configuration-file). |
|
|
193
|
+
| `-f`, `--format` | `m4a` | Audio format: `mp3`, `m4a` or `opus`. See [Audio formats](#audio-formats). |
|
|
194
|
+
| `--title-only` | off | Name files `Title.m4a` instead of `Artist - Title.m4a` |
|
|
169
195
|
| `--delay` | `1.0` | Seconds to wait between songs. Raise it for long lists. |
|
|
196
|
+
| `--spotify-client-id` | `$SPOTIFY_CLIENT_ID` | Spotify API client ID. See [Spotify credentials](#spotify-credentials). |
|
|
197
|
+
| `--spotify-client-secret` | `$SPOTIFY_CLIENT_SECRET` | Spotify API client secret. |
|
|
170
198
|
|
|
171
199
|
```bash
|
|
172
200
|
trackfetch songs.txt -o ~/Music/RoadTrip # save to a specific folder
|
|
173
201
|
trackfetch songs.txt -o /media/usb --title-only # short names for a car stereo
|
|
174
202
|
trackfetch big-list.txt --delay 3 # go easy on rate limits
|
|
203
|
+
trackfetch songs.txt --format opus # original YouTube audio, no re-encode
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Configuration file
|
|
207
|
+
|
|
208
|
+
Put your defaults in an INI file and forget the flags:
|
|
209
|
+
|
|
210
|
+
```ini
|
|
211
|
+
[trackfetch]
|
|
212
|
+
output = ~/Music/trackfetch
|
|
213
|
+
format = m4a
|
|
214
|
+
title_only = false
|
|
215
|
+
delay = 1.0
|
|
216
|
+
input = ~/songs.txt
|
|
217
|
+
spotify_client_id = your-client-id
|
|
218
|
+
spotify_client_secret = your-client-secret
|
|
175
219
|
```
|
|
176
220
|
|
|
221
|
+
Every option above can be set there, under the `[trackfetch]` section. The
|
|
222
|
+
precedence is always the same:
|
|
223
|
+
|
|
224
|
+
1. the command-line flag,
|
|
225
|
+
2. the config file,
|
|
226
|
+
3. the built-in default (the current directory for the output folder).
|
|
227
|
+
|
|
228
|
+
trackfetch looks for the config file in this order: the path given with
|
|
229
|
+
`--config`, the path in `$TRACKFETCH_CONFIG`, `trackfetch.ini` in the current
|
|
230
|
+
directory, `$XDG_CONFIG_HOME/trackfetch/trackfetch.ini` (usually
|
|
231
|
+
`~/.config/trackfetch/trackfetch.ini`) and `~/.trackfetch.ini`.
|
|
232
|
+
|
|
233
|
+
### Spotify credentials
|
|
234
|
+
|
|
235
|
+
trackfetch needs a Spotify client ID and secret to read the metadata. Give
|
|
236
|
+
them in whichever way suits you:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# in the config file
|
|
240
|
+
[trackfetch]
|
|
241
|
+
spotify_client_id = your-client-id
|
|
242
|
+
spotify_client_secret = your-client-secret
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
# in the environment
|
|
247
|
+
export SPOTIFY_CLIENT_ID='your-client-id'
|
|
248
|
+
export SPOTIFY_CLIENT_SECRET='your-client-secret'
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
# on the command line
|
|
253
|
+
trackfetch songs.txt --spotify-client-id your-id --spotify-client-secret your-secret
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The flag wins over the config file, and the config file wins over the
|
|
257
|
+
environment variable.
|
|
258
|
+
|
|
259
|
+
### Audio formats
|
|
260
|
+
|
|
261
|
+
| Format | What you get | Plays on |
|
|
262
|
+
| --- | --- | --- |
|
|
263
|
+
| `m4a` (default) | YouTube's **original** AAC audio when available, copied without re-encoding | Apple devices, iTunes, most players |
|
|
264
|
+
| `opus` | YouTube's **original** Opus audio, copied without re-encoding. The best quality you can get | most modern players, Android, browsers; not iTunes or older devices |
|
|
265
|
+
| `mp3` | YouTube's audio **re-encoded** to the best VBR MP3. A second lossy encode, so slightly below the source | everything, including old players and car stereos |
|
|
266
|
+
|
|
267
|
+
All three get the same tags and embedded cover art. `done.txt` doesn't track the format, so use a separate `-o` folder for each format.
|
|
268
|
+
|
|
177
269
|
### Input file
|
|
178
270
|
|
|
179
271
|
```text
|
|
@@ -182,14 +274,14 @@ Daft Punk - One More Time
|
|
|
182
274
|
Sufjan Stevens - Mystery of Love - Remastered ← only the first " - " separates artist and title
|
|
183
275
|
```
|
|
184
276
|
|
|
185
|
-
The separator is space, hyphen, space (` - `). Lines without it are skipped. See [`examples/songs.txt`](examples/songs.txt).
|
|
277
|
+
The separator is space, hyphen, space (` - `). Lines without it are skipped. See [`examples/songs.txt`](https://github.com/ByteMe6/trackfetch/blob/master/examples/songs.txt).
|
|
186
278
|
|
|
187
279
|
### Output folder
|
|
188
280
|
|
|
189
281
|
```text
|
|
190
|
-
|
|
191
|
-
├── Daft Punk - One More Time.
|
|
192
|
-
├── Radiohead - Paranoid Android.
|
|
282
|
+
./
|
|
283
|
+
├── Daft Punk - One More Time.m4a
|
|
284
|
+
├── Radiohead - Paranoid Android.m4a
|
|
193
285
|
├── done.txt ← finished songs, skipped on the next run
|
|
194
286
|
└── failed.txt ← "Artist - Title | reason" for each song that failed
|
|
195
287
|
```
|
|
@@ -197,7 +289,7 @@ The separator is space, hyphen, space (` - `). Lines without it are skipped. See
|
|
|
197
289
|
**Retry failures** by stripping the reasons from `failed.txt` and running it again:
|
|
198
290
|
|
|
199
291
|
```bash
|
|
200
|
-
cut -d '|' -f 1
|
|
292
|
+
cut -d '|' -f 1 ./failed.txt > retry.txt
|
|
201
293
|
trackfetch retry.txt
|
|
202
294
|
```
|
|
203
295
|
|
|
@@ -205,7 +297,7 @@ trackfetch retry.txt
|
|
|
205
297
|
|
|
206
298
|
### Cheat sheet
|
|
207
299
|
|
|
208
|
-
A [tldr](https://tldr.sh/) page is in [`docs/tldr/trackfetch.md`](docs/tldr/trackfetch.md). To use it with [tealdeer](https://github.com/tealdeer-rs/tealdeer), copy it into your custom pages folder as `trackfetch.page.md`.
|
|
300
|
+
A [tldr](https://tldr.sh/) page is in [`docs/tldr/trackfetch.md`](https://github.com/ByteMe6/trackfetch/blob/master/docs/tldr/trackfetch.md). To use it with [tealdeer](https://github.com/tealdeer-rs/tealdeer), copy it into your custom pages folder as `trackfetch.page.md`.
|
|
209
301
|
|
|
210
302
|
## How it works
|
|
211
303
|
|
|
@@ -282,11 +374,11 @@ pytest --cov=trackfetch
|
|
|
282
374
|
|
|
283
375
|
The tests stub every network and subprocess call, so they run offline in under a second. To build a standalone binary yourself, run `pip install pyinstaller && pyinstaller trackfetch.spec`; it ends up in `dist/`.
|
|
284
376
|
|
|
285
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
|
|
377
|
+
See [CONTRIBUTING.md](https://github.com/ByteMe6/trackfetch/blob/master/CONTRIBUTING.md) before opening a pull request.
|
|
286
378
|
|
|
287
379
|
### Releasing
|
|
288
380
|
|
|
289
|
-
Every push to `master` runs the tests on Linux, macOS and Windows. When `version` in [`pyproject.toml`](pyproject.toml) has no matching `vX.Y.Z` tag yet, the [Build & Release](.github/workflows/release.yml) workflow also builds all six binaries and publishes a release, using that version's section of [`CHANGELOG.md`](CHANGELOG.md) as the notes.
|
|
381
|
+
Every push to `master` runs the tests on Linux, macOS and Windows. When `version` in [`pyproject.toml`](https://github.com/ByteMe6/trackfetch/blob/master/pyproject.toml) has no matching `vX.Y.Z` tag yet, the [Build & Release](https://github.com/ByteMe6/trackfetch/blob/master/.github/workflows/release.yml) workflow also builds all six binaries and publishes a release, using that version's section of [`CHANGELOG.md`](https://github.com/ByteMe6/trackfetch/blob/master/CHANGELOG.md) as the notes.
|
|
290
382
|
|
|
291
383
|
To release, move the changes under `[Unreleased]` in the changelog to a new `## [X.Y.Z] - YYYY-MM-DD` section, bump `version`, and push.
|
|
292
384
|
|
|
@@ -299,4 +391,4 @@ trackfetch is for personal use with content you have the right to download. You
|
|
|
299
391
|
|
|
300
392
|
## License
|
|
301
393
|
|
|
302
|
-
trackfetch is free software, released under the [GNU General Public License v3.0 or later](LICENSE). You can use, study, change and share it. If you distribute a modified version, it must stay under the same license.
|
|
394
|
+
trackfetch is free software, released under the [GNU General Public License v3.0 or later](https://github.com/ByteMe6/trackfetch/blob/master/LICENSE). You can use, study, change and share it. If you distribute a modified version, it must stay under the same license.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "trackfetch"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.2.0"
|
|
8
8
|
description = "Turn a text file of songs into tagged MP3s: Spotify metadata and cover art, best-match YouTube audio via yt-dlp."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "GPL-3.0-or-later"
|