trav 2c5b5a575f Store track paths relative to the data dir for multi-machine sync
The library is going live as the canonical store, synced to a second machine
via Syncthing. Track locations were stored absolute and only remapped at import,
so on a second machine (where Syncthing mounts the folder at a different path)
every location would break.

- lintunes/paths.py: to_relative/to_absolute. Locations are stored relative to
  the data dir and resolved back on load, applied only at the json_storage
  boundary (save_tracks/load_library). Track.location stays absolute in memory,
  so the player, tagging, and art code are unchanged. The data dir and the music
  move together inside one synced tree, so paths resolve wherever it's mounted —
  no per-machine music_root config. Absolute paths in older library.json files
  still load (back-compat).
- tests/test_round15.py: helper round-trips, back-compat, and a machine-2
  scenario (save under root A, load the copied tree under root B).
- TASKS.md/tasks-done.md: mark the data-dir move + real import done; log the
  benign exit-time Qt/FFmpeg teardown segfault.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 16:43:57 -04:00
2026-06-26 21:12:01 -04:00
2026-06-26 21:12:01 -04:00
2026-06-26 21:13:06 -04:00
2026-06-26 21:12:01 -04:00
2026-06-26 21:12:01 -04:00

LinTunes

[screenshot]

an mp3 library manager and player for linux. Absolutely no guarantees, if it wrecks your itunes library or wipes your harddrive that's on you (maybe just have your LLM of choice review the software for bugs and vulns?).

Features

  • can import an iTunes 12 (untested on other versions) library

How it works

  • Your music files are never moved or rewritten (except when you edit tags).
  • The library lives as plain JSON files (library.json + one file per playlist) in a directory you choose — designed to be synced with Syncthing; sync conflicts are merged automatically on startup (play counts take the max, edits take the newest, playlists take the union).
  • Playback via Qt Multimedia/FFmpeg (mp3, m4a, flac). Media keys work through MPRIS. Scrobbling to last.fm is optional (Edit → Preferences).

Why

I have been a mac user for ~34 years. I gave up daily driving mac os in 2020. I figured Apple

but my music library even some tracks I got from napster all the way back in 2000 (I have since paid for!!)

Quickstart

pip install -e .          # PyQt6, mutagen, numpy, requests
# one-time import from iTunes (XML from iTunes 12.x: File > Library > Export Library)
lintunes --import-xml "iTunes Library.xml" \
         --music-root "/path/to/iTunes Media" \
         --data-dir /path/to/library-data --save-config
lintunes                  # run the app

Running it

The lintunes command only exists after pip install -e ., and it lives in ~/.local/bin, so that has to be on your PATH (it is by default on most distros). If lintunes isn't found, you can always run it straight from this checkout without installing — from the project directory:

python3 -m lintunes.main   # same thing the desktop launcher runs

Fedora/Debian note: you need the FFmpeg codecs for Qt Multimedia (qt6-qtmultimedia with ffmpeg, usually via RPM Fusion / regular apt).

LinTunes looks best with Century Gothic installed (~/.local/share/fonts/); if it's missing, you'll be asked to pick a font on first run.

Keys

Space play/pause · ←/→ previous/next · Ctrl+B column browser · Ctrl+I get info · Ctrl+, preferences · Ctrl+C/Ctrl+X/Ctrl+V copy/cut/paste tracks · double-click sidebar art for a big art window

App icon

packaging/install-desktop.sh installs the launcher entry and icon for your user (lets you pin LinTunes to the GNOME dash). The icon is just a file — replace packaging/lintunes.png (256×256 PNG) and re-run the script to use your own. GNOME caches icons, so if the old one lingers, log out and back in.

Unpinning from the dash does not uninstall LinTunes — it's still in the GNOME app grid (open Activities and search "LinTunes"). Right-click it there → Pin to Dash to get it back.

Development

python3 -m pytest tests/

spec.md is the original design brief; tasks*.md track what's built.

Description
No description provided
Readme 1.1 MiB
Languages
Python 99.7%
Shell 0.3%