A text file of songs in, tagged MP3s with cover art out. Spotify metadata, best-match YouTube audio via yt-dlp, resumable, cross-platform CLI.
Python
1
22 commits
updated Oct 8, 2026
English · Українська · Русский
Install · Quick start · Playlists from streaming services · Usage · How it works · Troubleshooting
trackfetch reads a plain text file with one Artist - Title per line. For every song it looks up the official metadata and cover art on Spotify, picks the best-matching audio on YouTube, and saves an MP3 with complete ID3 tags. Runs are resumable, so you can stop a 1,000-song list at any point and pick it up later.
done.txt and skipped next time. Failures go to failed.txt with the reason.Pick one. Homebrew and Nix also install yt-dlp, FFmpeg and Deno for you; with the other methods, install them yourself (see below). Every method needs a free Spotify API key (set it up below).
macOS and Linux:
brew install ByteMe6/tap/trackfetch
On Intel Macs, Homebrew builds the dependencies from source, so the first install takes a while.
Linux and Apple Silicon Macs, with flakes enabled:
nix run github:ByteMe6/trackfetch -- songs.txt # run without installing
nix profile install github:ByteMe6/trackfetch # install
pipx install trackfetch
No Python required. Download the file for your system from the latest release:
| x86_64 | ARM64 | |
|---|---|---|
| Linux | trackfetch-linux-x86_64 | trackfetch-linux-arm64 |
| macOS | trackfetch-macos-x86_64 (Intel) | trackfetch-macos-arm64 (Apple Silicon) |
| Windows | trackfetch-windows-x86_64.exe | trackfetch-windows-arm64.exe |
On Linux and macOS, make it executable and put it on your PATH:
chmod +x trackfetch-linux-x86_64
sudo mv trackfetch-linux-x86_64 /usr/local/bin/trackfetch
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.
git clone https://github.com/ByteMe6/trackfetch.git
cd trackfetch
python -m venv .venv && source .venv/bin/activate
pip install -e .
With pipx, a standalone binary or a source install, put these on your PATH:
| Tool | Used for | Install |
|---|---|---|
| yt-dlp | Searching and downloading from YouTube | pipx install yt-dlp · brew install yt-dlp · winget install yt-dlp |
| FFmpeg | Converting the audio to MP3 | sudo apt install ffmpeg · brew install ffmpeg · winget install ffmpeg |
| Deno | JavaScript runtime that yt-dlp needs for YouTube | curl -fsSL https://deno.land/install.sh | sh · brew install deno · winget install DenoLand.Deno |
trackfetch uses Spotify's Client Credentials flow: no Spotify login, no access to your account.
http://127.0.0.1:8888/callback. trackfetch never uses it, but the form requires one.export SPOTIFY_CLIENT_ID='your-client-id'
export SPOTIFY_CLIENT_SECRET='your-client-secret'
set -Ux SPOTIFY_CLIENT_ID 'your-client-id'
set -Ux SPOTIFY_CLIENT_SECRET 'your-client-secret'
[Environment]::SetEnvironmentVariable('SPOTIFY_CLIENT_ID', 'your-client-id', 'User')
[Environment]::SetEnvironmentVariable('SPOTIFY_CLIENT_SECRET', 'your-client-secret', 'User')
[!CAUTION] Spotipy caches the access token in a
.cachefile in the current directory. Don't commit it or share it.
cat > songs.txt <<'EOF'
# My playlist
Daft Punk - One More Time
Radiohead - Paranoid Android
Korol i Shut - Мёртвый Анархист
EOF
trackfetch songs.txt
The files land in ~/Music/trackfetch/.
You don't have to type the list by hand. TuneMyMusic exports playlists from Spotify, Apple Music, YouTube Music, Deezer, Tidal, SoundCloud and other services to a text file that trackfetch reads as is:
trackfetch "My Playlist.txt" -o ~/Music/"My Playlist"
trackfetch [-h] [-o OUTPUT] [-f {mp3,m4a,opus}] [--title-only] [--delay DELAY] input
| Option | Default | What it does |
|---|---|---|
input | required | Text file with one Artist - Title per line |
-o, --output | ~/Music/trackfetch | Folder to save the files in. It's created if it doesn't exist. |
-f, --format | mp3 | Audio format: mp3, m4a or opus. See Audio formats. |
--title-only | off | Name files Title.mp3 instead of Artist - Title.mp3 |
--delay | 1.0 | Seconds to wait between songs. Raise it for long lists. |
trackfetch songs.txt -o ~/Music/RoadTrip # save to a specific folder
trackfetch songs.txt -o /media/usb --title-only # short names for a car stereo
trackfetch big-list.txt --delay 3 # go easy on rate limits
trackfetch songs.txt --format opus # original YouTube audio, no re-encode
| Format | What you get | Plays on |
|---|---|---|
mp3 (default) | 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 |
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 |
m4a | YouTube's original AAC audio when available, copied without re-encoding | Apple devices, iTunes, most players |
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.
# Lines starting with "#" are comments. Blank lines are ignored.
Daft Punk - One More Time
Sufjan Stevens - Mystery of Love - Remastered ← only the first " - " separates artist and title
The separator is space, hyphen, space (-). Lines without it are skipped. See examples/songs.txt.
~/Music/trackfetch/
├── Daft Punk - One More Time.mp3
├── Radiohead - Paranoid Android.mp3
├── done.txt ← finished songs, skipped on the next run
└── failed.txt ← "Artist - Title | reason" for each song that failed
Retry failures by stripping the reasons from failed.txt and running it again:
cut -d '|' -f 1 ~/Music/trackfetch/failed.txt > retry.txt
trackfetch retry.txt
Re-download a song by deleting its MP3 and its line in done.txt.
A tldr page is in docs/tldr/trackfetch.md. To use it with tealdeer, copy it into your custom pages folder as trackfetch.page.md.
Find the song on Spotify. trackfetch searches for artist:"…" track:"…" and falls back to a looser search if nothing comes back. Every result is scored by fuzzy similarity, 70% title and 30% artist, and the best one supplies the official spelling, all credited artists, the album details and the cover.
Find the audio on YouTube. It searches YouTube for the official artist and title and scores the top five results:
| Signal | Effect on the score |
|---|---|
| Similarity to the song title | × 0.70 |
| Similarity to the artist | × 0.30 |
official audio in the video title | +0.08 |
audio in the video title | +0.03 |
cover, karaoke, караоке, nightcore, sped up, slowed, remix, reaction | −0.20 each |
Download. yt-dlp downloads only the winning video and converts it to MP3.
Tag. trackfetch replaces any existing tags with these ID3v2.3 frames:
| Frame | Content |
|---|---|
TIT2 | Title |
TPE1 | Artists |
TALB | Album |
TPE2 | Album artist |
TRCK | Track number |
TDRC | Release date |
APIC | Front cover, up to 640×640 |
If Spotify has no match, the song is still downloaded and tagged with the artist and title from your file.
ERROR: Spotify credentials not found.SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET aren't set in this terminal. See Spotify credentials.
no YouTube result or yt-dlp download failedYouTube changes often. Update yt-dlp first with pipx upgrade yt-dlp or yt-dlp -U. Recent versions of yt-dlp also need Deno: check that deno --version works in the same terminal.
ERROR: Postprocessing: ffprobe and ffmpeg not foundInstall FFmpeg and check that ffmpeg -version works in the same terminal.
Make the line in your file more specific, for example by using the exact title as Spotify spells it. Then delete the MP3 and its line in done.txt and run trackfetch again. If it keeps picking the wrong upload, open an issue with the YouTube score lines from the output.
Raise --delay, for example to 3. Stopped runs continue where they left off.
pip install -e ".[test]" ruff
ruff check .
pytest --cov=trackfetch
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/.
See CONTRIBUTING.md before opening a pull request.
Every push to master runs the tests on Linux, macOS and Windows. When version in pyproject.toml has no matching vX.Y.Z tag yet, the Build & Release workflow also builds all six binaries and publishes a release, using that version's section of CHANGELOG.md as the notes.
To release, move the changes under [Unreleased] in the changelog to a new ## [X.Y.Z] - YYYY-MM-DD section, bump version, and push.
[!IMPORTANT] Bump the version in a commit that doesn't edit
.github/workflows/. GitHub doesn't let the workflow token tag a commit that changes workflow files, so the release step would fail with a 403.
trackfetch is for personal use with content you have the right to download. You are responsible for following copyright law where you live and the terms of service of YouTube and Spotify. trackfetch isn't affiliated with or endorsed by Spotify, YouTube or TuneMyMusic. If you can, support the artists you listen to.
trackfetch is free software, released under the GNU General Public License v3.0 or later. You can use, study, change and share it. If you distribute a modified version, it must stay under the same license.
A text file of songs in, tagged MP3s with cover art out. Spotify metadata, best-match YouTube audio via yt-dlp, resumable, cross-platform CLI.
Python
1
22 commits
updated Oct 8, 2026
English · Українська · Русский
Install · Quick start · Playlists from streaming services · Usage · How it works · Troubleshooting
trackfetch reads a plain text file with one Artist - Title per line. For every song it looks up the official metadata and cover art on Spotify, picks the best-matching audio on YouTube, and saves an MP3 with complete ID3 tags. Runs are resumable, so you can stop a 1,000-song list at any point and pick it up later.
done.txt and skipped next time. Failures go to failed.txt with the reason.Pick one. Homebrew and Nix also install yt-dlp, FFmpeg and Deno for you; with the other methods, install them yourself (see below). Every method needs a free Spotify API key (set it up below).
macOS and Linux:
brew install ByteMe6/tap/trackfetch
On Intel Macs, Homebrew builds the dependencies from source, so the first install takes a while.
Linux and Apple Silicon Macs, with flakes enabled:
nix run github:ByteMe6/trackfetch -- songs.txt # run without installing
nix profile install github:ByteMe6/trackfetch # install
pipx install trackfetch
No Python required. Download the file for your system from the latest release:
| x86_64 | ARM64 | |
|---|---|---|
| Linux | trackfetch-linux-x86_64 | trackfetch-linux-arm64 |
| macOS | trackfetch-macos-x86_64 (Intel) | trackfetch-macos-arm64 (Apple Silicon) |
| Windows | trackfetch-windows-x86_64.exe | trackfetch-windows-arm64.exe |
On Linux and macOS, make it executable and put it on your PATH:
chmod +x trackfetch-linux-x86_64
sudo mv trackfetch-linux-x86_64 /usr/local/bin/trackfetch
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.
git clone https://github.com/ByteMe6/trackfetch.git
cd trackfetch
python -m venv .venv && source .venv/bin/activate
pip install -e .
With pipx, a standalone binary or a source install, put these on your PATH:
| Tool | Used for | Install |
|---|---|---|
| yt-dlp | Searching and downloading from YouTube | pipx install yt-dlp · brew install yt-dlp · winget install yt-dlp |
| FFmpeg | Converting the audio to MP3 | sudo apt install ffmpeg · brew install ffmpeg · winget install ffmpeg |
| Deno | JavaScript runtime that yt-dlp needs for YouTube | curl -fsSL https://deno.land/install.sh | sh · brew install deno · winget install DenoLand.Deno |
trackfetch uses Spotify's Client Credentials flow: no Spotify login, no access to your account.
http://127.0.0.1:8888/callback. trackfetch never uses it, but the form requires one.export SPOTIFY_CLIENT_ID='your-client-id'
export SPOTIFY_CLIENT_SECRET='your-client-secret'
set -Ux SPOTIFY_CLIENT_ID 'your-client-id'
set -Ux SPOTIFY_CLIENT_SECRET 'your-client-secret'
[Environment]::SetEnvironmentVariable('SPOTIFY_CLIENT_ID', 'your-client-id', 'User')
[Environment]::SetEnvironmentVariable('SPOTIFY_CLIENT_SECRET', 'your-client-secret', 'User')
[!CAUTION] Spotipy caches the access token in a
.cachefile in the current directory. Don't commit it or share it.
cat > songs.txt <<'EOF'
# My playlist
Daft Punk - One More Time
Radiohead - Paranoid Android
Korol i Shut - Мёртвый Анархист
EOF
trackfetch songs.txt
The files land in ~/Music/trackfetch/.
You don't have to type the list by hand. TuneMyMusic exports playlists from Spotify, Apple Music, YouTube Music, Deezer, Tidal, SoundCloud and other services to a text file that trackfetch reads as is:
trackfetch "My Playlist.txt" -o ~/Music/"My Playlist"
trackfetch [-h] [-o OUTPUT] [-f {mp3,m4a,opus}] [--title-only] [--delay DELAY] input
| Option | Default | What it does |
|---|---|---|
input | required | Text file with one Artist - Title per line |
-o, --output | ~/Music/trackfetch | Folder to save the files in. It's created if it doesn't exist. |
-f, --format | mp3 | Audio format: mp3, m4a or opus. See Audio formats. |
--title-only | off | Name files Title.mp3 instead of Artist - Title.mp3 |
--delay | 1.0 | Seconds to wait between songs. Raise it for long lists. |
trackfetch songs.txt -o ~/Music/RoadTrip # save to a specific folder
trackfetch songs.txt -o /media/usb --title-only # short names for a car stereo
trackfetch big-list.txt --delay 3 # go easy on rate limits
trackfetch songs.txt --format opus # original YouTube audio, no re-encode
| Format | What you get | Plays on |
|---|---|---|
mp3 (default) | 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 |
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 |
m4a | YouTube's original AAC audio when available, copied without re-encoding | Apple devices, iTunes, most players |
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.
# Lines starting with "#" are comments. Blank lines are ignored.
Daft Punk - One More Time
Sufjan Stevens - Mystery of Love - Remastered ← only the first " - " separates artist and title
The separator is space, hyphen, space (-). Lines without it are skipped. See examples/songs.txt.
~/Music/trackfetch/
├── Daft Punk - One More Time.mp3
├── Radiohead - Paranoid Android.mp3
├── done.txt ← finished songs, skipped on the next run
└── failed.txt ← "Artist - Title | reason" for each song that failed
Retry failures by stripping the reasons from failed.txt and running it again:
cut -d '|' -f 1 ~/Music/trackfetch/failed.txt > retry.txt
trackfetch retry.txt
Re-download a song by deleting its MP3 and its line in done.txt.
A tldr page is in docs/tldr/trackfetch.md. To use it with tealdeer, copy it into your custom pages folder as trackfetch.page.md.
Find the song on Spotify. trackfetch searches for artist:"…" track:"…" and falls back to a looser search if nothing comes back. Every result is scored by fuzzy similarity, 70% title and 30% artist, and the best one supplies the official spelling, all credited artists, the album details and the cover.
Find the audio on YouTube. It searches YouTube for the official artist and title and scores the top five results:
| Signal | Effect on the score |
|---|---|
| Similarity to the song title | × 0.70 |
| Similarity to the artist | × 0.30 |
official audio in the video title | +0.08 |
audio in the video title | +0.03 |
cover, karaoke, караоке, nightcore, sped up, slowed, remix, reaction | −0.20 each |
Download. yt-dlp downloads only the winning video and converts it to MP3.
Tag. trackfetch replaces any existing tags with these ID3v2.3 frames:
| Frame | Content |
|---|---|
TIT2 | Title |
TPE1 | Artists |
TALB | Album |
TPE2 | Album artist |
TRCK | Track number |
TDRC | Release date |
APIC | Front cover, up to 640×640 |
If Spotify has no match, the song is still downloaded and tagged with the artist and title from your file.
ERROR: Spotify credentials not found.SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET aren't set in this terminal. See Spotify credentials.
no YouTube result or yt-dlp download failedYouTube changes often. Update yt-dlp first with pipx upgrade yt-dlp or yt-dlp -U. Recent versions of yt-dlp also need Deno: check that deno --version works in the same terminal.
ERROR: Postprocessing: ffprobe and ffmpeg not foundInstall FFmpeg and check that ffmpeg -version works in the same terminal.
Make the line in your file more specific, for example by using the exact title as Spotify spells it. Then delete the MP3 and its line in done.txt and run trackfetch again. If it keeps picking the wrong upload, open an issue with the YouTube score lines from the output.
Raise --delay, for example to 3. Stopped runs continue where they left off.
pip install -e ".[test]" ruff
ruff check .
pytest --cov=trackfetch
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/.
See CONTRIBUTING.md before opening a pull request.
Every push to master runs the tests on Linux, macOS and Windows. When version in pyproject.toml has no matching vX.Y.Z tag yet, the Build & Release workflow also builds all six binaries and publishes a release, using that version's section of CHANGELOG.md as the notes.
To release, move the changes under [Unreleased] in the changelog to a new ## [X.Y.Z] - YYYY-MM-DD section, bump version, and push.
[!IMPORTANT] Bump the version in a commit that doesn't edit
.github/workflows/. GitHub doesn't let the workflow token tag a commit that changes workflow files, so the release step would fail with a 403.
trackfetch is for personal use with content you have the right to download. You are responsible for following copyright law where you live and the terms of service of YouTube and Spotify. trackfetch isn't affiliated with or endorsed by Spotify, YouTube or TuneMyMusic. If you can, support the artists you listen to.
trackfetch is free software, released under the GNU General Public License v3.0 or later. You can use, study, change and share it. If you distribute a modified version, it must stay under the same license.