Skip to main content
trackfetch: a text file of songs in, tagged MP3s with cover art out

Latest release Build status Python 3.10+ Linux, macOS and Windows on x86_64 and ARM64 License: GPL-3.0-or-later

English · Українська · Русский

Install · Quick start · Playlists from streaming services · Usage · How it works · Troubleshooting


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

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.

  • Exact metadata. The title, every credited artist, album, album artist, track number and release date come from Spotify, not from a YouTube video title.
  • Album art. The largest Spotify cover is embedded in every file.
  • The right upload. trackfetch scores five YouTube results per song. Official audio wins; covers, karaoke, nightcore, sped-up, slowed, remix and reaction videos lose.
  • Best quality. yt-dlp extracts the audio as the highest-quality VBR MP3.
  • Resumable. Finished songs are logged in done.txt and skipped next time. Failures go to failed.txt with the reason.
  • Plays everywhere. Tags are written as ID3v2.3, which Windows Explorer, Apple Music, Android, car stereos and most other players read.
  • Any alphabet. Cyrillic and other non-Latin titles are matched correctly and kept in filenames. Characters that are illegal in filenames are replaced.
  • No Python needed. Standalone binaries for Linux, macOS and Windows on x86_64 and ARM64.

Install

trackfetch needs these tools on your PATH, plus a free Spotify API key (set it up below):

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

Standalone binary

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.

pipx

pipx install git+https://github.com/ByteMe6/trackfetch.git

From source

git clone https://github.com/ByteMe6/trackfetch.git
cd trackfetch
python -m venv .venv && source .venv/bin/activate
pip install -e .

Spotify credentials

trackfetch uses Spotify's Client Credentials flow: no Spotify login, no access to your account.

  1. Open the Spotify Developer Dashboard and click Create app.
  2. Enter any name and description. For the redirect URI, enter http://127.0.0.1:8888/callback. trackfetch never uses it, but the form requires one.
  3. Open the app's Settings and copy the Client ID and Client Secret.
  4. Set them as environment variables:
bash / zsh
export SPOTIFY_CLIENT_ID='your-client-id'
export SPOTIFY_CLIENT_SECRET='your-client-secret'
fish
set -Ux SPOTIFY_CLIENT_ID 'your-client-id'
set -Ux SPOTIFY_CLIENT_SECRET 'your-client-secret'
PowerShell
[Environment]::SetEnvironmentVariable('SPOTIFY_CLIENT_ID', 'your-client-id', 'User')
[Environment]::SetEnvironmentVariable('SPOTIFY_CLIENT_SECRET', 'your-client-secret', 'User')

Quick start

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/.

Playlists from streaming services

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:

  1. On tunemymusic.com, choose the service your playlist is on as the source.
  2. Select the playlists you want.
  3. Choose Export to file as the destination and save it as TXT.
  4. Run trackfetch on the exported file:
trackfetch "My Playlist.txt" -o ~/Music/"My Playlist"

Usage

trackfetch [-h] [-o OUTPUT] [--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 MP3s in. It's created if it doesn't exist.
--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

Input file

# 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.

Output folder

~/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.

Cheat sheet

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.

How it works

  1. 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.

  2. 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
  3. Download. yt-dlp downloads only the winning video and converts it to MP3.

  4. 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.

Troubleshooting

ERROR: Spotify credentials not found.

SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET aren't set in this terminal. See Spotify credentials.

Every song fails with no YouTube result or yt-dlp download failed

YouTube 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 found

Install FFmpeg and check that ffmpeg -version works in the same terminal.

The wrong version of a song was downloaded

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.

HTTP 429 or other rate-limit errors

Raise --delay, for example to 3. Stopped runs continue where they left off.

Development

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.

Releasing

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.

Disclaimer

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.

License

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.

Metadata

Release files for trackfetch 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for trackfetch 2.0.0
File Size Uploaded
trackfetch-2.0.0.tar.gz 34.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for trackfetch 2.0.0
File Interpreter ABI Platform
trackfetch-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 58.3 kB

Release files / trackfetch-2.0.0.tar.gz

Download URL trackfetch-2.0.0.tar.gz
Size 34.6 kB
Tags Source
SHA-256 checksum
How to use checksums
3dfb17bf7603841ad70b49b990792638720f6a35d6ef84db050d26182f2d45e0
BLAKE2b-256 checksum
How to use checksums
6cc6f99b9992c1eb9ed3e94783c6a9003258aa50570c28f39831318d22a15591
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release files / trackfetch-2.0.0-py3-none-any.whl

Download URL trackfetch-2.0.0-py3-none-any.whl
Size 23.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b90dfd896fe6d3ca50f7a8ef388684ade03cc39137e2b1657ba465ad7b8708df
BLAKE2b-256 checksum
How to use checksums
8dff8ad40f284c300c107b99c4955a20bbca4ce856d15ee14e4be442dc04c6c9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page