travandClaude Opus 5 c8de124543 v0.13.0: the merge report says who, what, and where
The merge window kept reporting "Order kept from this machine (most recently
edited)" for playlists edited on the other machine. Three defects, confirmed
against the real snapshots in .resolved/:

* "this machine" was inferred from which copy held the plain filename. That is
  Syncthing's call, not a statement about authorship — it sets the local copy
  aside as readily as a remote one. In the 9:34 PM `* a fresh master` merge the
  copy labelled "the other machine" was this machine's own 3:15 PM merge output,
  so the label was exactly backwards. The 7-char device ID in the conflict
  filename — the only real evidence — was matched by a bare \w+ and deleted with
  the file. New sync_identity.py decodes it against Syncthing's config.xml and
  works out which device is us from cert.pem.

* The decision leaned local. date_modified was only consulted when *both* copies
  had one, and an iTunes playlist never reordered here has none — so the honest
  comparison was skipped exactly when one machine had edited and the other
  hadn't. A stamped copy now beats an unstamped one; mtime is the fallback only
  when neither side has ever been edited. And every merge used to rewrite the
  file it kept whether or not anything changed, freshening its mtime while the
  conflict file kept its origin's: a ratchet. No-op merges write nothing, and a
  merge whose result is a union neither copy had stamps date_modified, so the
  other machine adopts it instead of trading the same 19 tracks back and forth.

* Nothing was actionable. Re-inserted tracks are now named with their position
  ("Pola — Abeille -> position 24, after ..."), six in the window and all of
  them in what-changed.txt at the top of the backup snapshot, alongside the real
  conflict filename and its device. Tracks only this copy has are reported too
  rather than resurrected in silence.

Also fixed while in here: a rename or folder move made elsewhere was discarded
by every merge (only track_ids and settings were adopted); _reconcile_playlist
asserted the local edit was newer and never checked, so a reorder synced in from
the other machine was undone and flushed back to disk, and the branch reaching
it was gated on a dirty flag that a column drag sets; and _merge_metadata
decided the music folder from whichever copy an mtime coin flip had kept.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y9ZEFi4qNJ39FMiBtiAxy2
2026-08-27 15:16:49 -04:00
2026-06-26 21:12:01 -04:00
2026-08-22 14:07:47 -04:00
2026-06-26 21:12:01 -04:00

LinTunes

LinTunes

trave notes

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?).

I have been a mac user for ~34 years. I gave up daily driving mac os in 2021. After 5 years I got tired of my music being solely on my desktop so I vibe-coded this replacement with all the features I wanted from iTunes.

docs written by llms and cleaned up by me:

Features

Library

  • Sortable track list with 24 optional columns (plays, skips, rating, BPM, bit rate, date added…) — column choice, sort and widths are remembered per playlist.
  • Keeps track of date song was last played
  • Column browser (Ctrl+B) — filter by genre / artist / album.
  • Instant search over name, artist, album, composer and more.
  • Get Info (Ctrl+I) edits tags on one track or a whole selection at once, writing them back to the file. Artwork can be pasted, dragged in, or fetched automatically (Download Album Art…, via the iTunes Search API).
  • Rename an artist or album and the files move themselves into a tidy Artist/Album/ tree.
  • Undo/redo everything (Ctrl+Z / Ctrl+Shift+Z) — edits, deletes, reorders.
  • Deleting a track sends the file to your desktop Trash, never rm.

Playlists

  • Regular playlists, folders, and smart playlists (auto-updating, with a full rule editor).
  • Drag and drop to reorder or insert at a spot; duplicate-aware adds; copy/cut/paste tracks between playlists.

Playing

  • mp3, m4a, flac, aac, wav, aiff.
  • Shuffle, per-track start/stop times, play counts, skip counts and ratings.
  • Media keys work, plus Space and ←/→ anywhere in the app.
  • Live spectrum visualizer — click it to cycle bright / dim / off.
  • last.fm scrobbling
  • Theming: highlight color, UI scale, and custom colors

Sending music elsewhere

  • Chromecast
  • Export a playlist (File ▸ Export Playlist…) — either a folder of Artist - Title.mp3 files plus an .m3u, or a self-contained index.html you can upload anywhere.
  • Sync a playlist to a Rabbit R1 over MTP, .m3u included.

Living on more than one machine

  • The whole library is plain JSON, designed to be synced with Syncthing. Sync conflicts merge themselves on startup.
  • The version number in the status bar checks for updates (via git) and you can click it to update to the latest version.

Never used a terminal? Start here

If the commands below look intimidating, they're friendlier than they look.

The terminal is a window where you type commands instead of clicking. On most Linux desktops you open it by pressing the Super key (the one with the Windows/Command logo) and typing "terminal". (On a Mac it's Terminal, from Spotlight; on Windows it's PowerShell, from the Start menu — but read the Mac/Windows note under What you need first.) Everything in a grey box below is meant to be copied and pasted into that window — paste it, press Enter, done. You don't need to understand a command to run it.

Two bits of shorthand that show up everywhere:

  • ~ means your home folder. ~/Music is just the Music folder inside your home folder — the same one your file manager shows you.
  • Your "distro" is which flavour of Linux you're running (Ubuntu, Fedora, Debian, Mint…). If you don't know, it's almost certainly Ubuntu or Mint, and you want the Debian/Ubuntu instructions below.

If a command fails, nothing is broken — copy the error into a search engine, or ask whoever pointed you here. You cannot damage your music by running these.

What you need

Linux, Python, and a handful of free add-ons. Nothing to compile, no account to make, no database to set up, nothing that phones home. LinTunes keeps your library as ordinary files on your own computer.

In detail:

  • Linux with Python 3.11 or newer (already installed on essentially every Linux machine — check with python3 --version)
  • PyQt6 (including its QtMultimedia part), mutagen, numpy, requests
  • The FFmpeg codecs for Qt Multimedia. Without these the window opens fine but nothing plays — so if you get a silent LinTunes, this is the thing that's missing. They come from qt6-qtmultimedia built with ffmpeg (Fedora: enable RPM Fusion; Debian/Ubuntu: it comes along with python3-pyqt6.qtmultimedia).

On a Mac or on Windows? LinTunes is only built and tested on Linux and that's not changing — but very little of it is actually Linux-specific: Python, PyQt6, mutagen and the rest run everywhere, and your library is just JSON files. On macOS it will most likely run as-is — clone the repo, pip install -e ., then python3 -m lintunes.main — with three things quietly missing: media keys and the now-playing widget, the keep-the-machine-awake-while-playing bit (both of those go through a Linux-only service), and Rabbit R1 sync. Deleting a track would also drop the file in a Linux-style .Trash folder rather than Finder's Trash — recoverable, but not where you'd look — and there's no .app or dock icon, so you'd launch it from Terminal. Windows needs a little real work first: a couple of places call a POSIX-only function (os.getuid(), in lintunes/trash.py and lintunes/device_sync.py), so deleting a track would error out until someone routes that through the Recycle Bin instead — a small patch, not a rewrite. If you get either one going, lmk!!

Installing

Pick whichever fits your distro:

  • pip — simplest where your distro allows it; also puts a lintunes command in ~/.local/bin:
    pip install -e .
    
  • Debian/Ubuntu — system pip is locked down (the externally-managed-environment / PEP 668 error), so install the deps from apt and skip pip entirely:
    sudo apt install python3-pyqt6 python3-pyqt6.qtmultimedia \
                     python3-mutagen python3-numpy python3-requests
    
    (If python3-pyqt6.qtmultimedia isn't found, apt search python3-pyqt6 and grab the multimedia one.) There's no lintunes command this way — run it with python3 -m lintunes.main.
  • venv — isolated:
    python3 -m venv .venv && .venv/bin/pip install -e .
    
    Run with .venv/bin/lintunes. One caveat: the dock launcher (see App icon) uses the system python, which can't see a venv's packages — so for the icon to work, edit the installed lintunes.desktop's Exec= line to the absolute venv path (…/lintunes/.venv/bin/lintunes %F).

You never strictly need pip or the lintunes command. As long as those four deps import, python3 -m lintunes.main from the checkout runs the app — which is exactly what the desktop launcher does.

Below, lintunes and python3 -m lintunes.main are interchangeable — use whichever your install gave you.

Starting from scratch (no iTunes needed)

Tell it where to keep the library, once:

mkdir -p ~/Music/lintunes                              # the library lives here
lintunes --data-dir ~/Music/lintunes --save-config     # remembers it from now on

Then just run lintunes. The first time it opens, it asks one question — where to keep your music — and suggests ~/Music. Pick a folder, or click Not Now and set it later in Edit ▸ Preferences.

Now add music: File ▸ Add Files to Library (Ctrl+O), or drag files and folders straight onto the window. Whole folders are fine.

Your songs are copied into the folder you chose and filed by artist and album.

Importing an existing iTunes library

In iTunes 12.x: File ▸ Library ▸ Export Library to get the XML, then:

lintunes --import-xml "iTunes Library.xml" \
    --music-root "/path/to/iTunes Media" \
    --data-dir /path/to/library-data --save-config
lintunes                                  # from now on, just run it

This brings across your tracks, playlists, playlist folders and smart playlists (auto-updating and still editable), along with play counts, ratings and dates. Mac paths are remapped to --music-root automatically, including case/accent differences. Anything it couldn't find is written to import_missing_files.txt in your data dir. Untested on iTunes versions other than 12.

Running it

lintunes            # uses the saved config

The desktop launcher runs LinTunes with no arguments, so it always uses whatever is saved in ~/.config/lintunes/config.json

Keys

Space play/pause · ←/→ previous/next · Ctrl+B column browser · Ctrl+I get info · Ctrl+L go to current song · Ctrl+O add files · Ctrl+N new playlist (Ctrl+Alt+N smart, Ctrl+Shift+N folder) · Ctrl+Z undo · Ctrl+, preferences · Ctrl+C/Ctrl+X/Ctrl+V copy/cut/paste tracks · double-click sidebar art for a big art window

Running on a second machine (Syncthing)

Keep the data dir (which holds library.json, the playlists, and preferences.json) inside your Syncthing-shared music folder, so the music and the library travel together. Track paths are stored relative to the data dir, so the library resolves correctly no matter where each machine mounts the shared folder — you never edit anything inside the library to move it.

~/.config/lintunes/config.json is per-machine (it is not synced), so on a new machine you just tell LinTunes where the synced folder landed:

# 1. get the code, then install the deps (see "Installing" above: pip / apt / venv)
git clone ssh://git@git.autonomic.zone:2222/trav/lintunes.git
cd lintunes

# 2. let Syncthing finish replicating the music folder, then point the config at
#    THIS machine's paths and launch — no re-import, the data is already synced:
python3 -m lintunes.main --data-dir "/path/to/synced/music/lintunes" --save-config

# 3. install the launcher + icon, then pin it (see "App icon" below)
bash packaging/install-desktop.sh

(Step 2 just writes data_dir into ~/.config/lintunes/config.json — you can also create that file by hand. After it's saved, every launch, including the dock icon, uses the synced library automatically.)

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.

S
Description
No description provided
Readme
2.4 MiB
Languages
Python 98.6%
JavaScript 0.6%
CSS 0.4%
Go Template 0.2%
Shell 0.2%