# LinTunes ![LinTunes](screenshot.png) ## 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`. - **Identify Track…** listens to a song (Chromaprint + AcoustID) and proposes its real title, artist, album and *original* release year — and when the internet has never heard of it, reads the answer out of the filename. - **Import from URL…** (File menu) — paste a link and the song is downloaded (yt-dlp), filed, and handed straight to Identify Track. - Album art is searched on both iTunes and **Deezer**, can be pasted onto a whole album's worth of tracks at once, and clicking the blank cover in the sidebar goes looking for it. - Right-click any song to **look it up on YouTube**. - Every song you add is stamped with the day you added it. **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. - Click a selected playlist's name to rename it. - New libraries start with a **New Tracks** smart playlist (everything added in the last 3 months). **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 - A soft emboss on every button that follows your 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. - **andTunes**, a small music player for the Rabbit R1 that LinTunes installs and fills from the Connections menu — and the plays you rack up on the Rabbit come back into your library. **Living on more than one machine** - The whole library is **plain JSON**, designed to be synced with [Syncthing](https://syncthing.net). 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. - Play counts can never conflict (each machine keeps its own tally), a song you remove on one machine stays removed, and the merge report only speaks up when something you'd care about happened. **Sharing with friends (Cassette)** - Share parts of your library with a friend — one pasted code connects you, over [Syncthing](https://syncthing.net), with no server anywhere. - You choose exactly what each friend can see: the whole library, or just certain artists, albums, songs and playlists. - Browse a friend's library and press the little cassette buttons on anything you'd like; those songs arrive in your library by themselves. - Follow a friend's playlist and play it right from your own sidebar. ## 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`: ```sh 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: ```sh 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: ```sh 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: ```sh 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: ```sh 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 ```sh 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: ```sh # 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 ```sh python3 -m pytest tests/ ``` `spec.md` is the original design brief; `tasks*.md` track what's built. ## Sharing music with friends (Cassette) Cassette lets you and a friend peek at each other's libraries and ask for songs. There's no server and no account: your two computers talk directly through a free program called **Syncthing**, and LinTunes just tells Syncthing what to do. Neither of you can change or delete anything on the other's computer. You'll both need LinTunes and Syncthing. You **don't** need to be online at the same time — things simply finish the next time both computers are on. ### 1. Install and start Syncthing (once) Open a terminal (see *Never used a terminal?* above) and paste the line for your distro: ```sh sudo apt install syncthing # Debian / Ubuntu / Mint sudo dnf install syncthing # Fedora ``` Then start it, and have it start by itself every time you log in: ```sh systemctl --user enable --now syncthing ``` To check it's running, open in your web browser — you should see Syncthing's page. You don't have to do anything there; the default settings (including "global discovery" and "relaying", which let two computers find each other across the internet) are exactly right. > **Already using Syncthing to keep your library on two of your own > computers?** Great — nothing changes. Only one of your computers handles > friends; LinTunes asks which the first time (see below), and songs it brings > in reach your other computers the usual way. ### 2. Connect with a friend One of you **invites**, the other **pastes**. It doesn't matter who. **If you're inviting:** in LinTunes choose **Connections ▸ Share Library with a Friend…**. The very first time, LinTunes asks *"Use this computer for sharing with friends?"* — say **Yes**. You'll get an **invite code**: a long line starting with `LTC1.` Press **Copy Code** and send it to your friend however you like (text, email, chat). Each code works for **one** friend, once. **If you were sent a code:** choose **Connections ▸ Add Friend Library…**, paste the whole code into the box, and press **Connect**. That's it. When the two computers have found each other, you'll see **"*Name* is now connected."** at the bottom of the window. This can take a minute — or, if the other computer is off, however long until it's back on. You can see how it's going any time in **Connections ▸ Sync Settings…**: pick your friend in the list on the left. Invites nobody has used yet are listed on the **General** page, with a **Cancel Invite** button each. ### 3. Choose what your friend can see **Nothing is shared until you say so.** In **Connections ▸ Sync Settings…**, click your friend's name on the left. Below their connection status are two sections: - **Library** — tick the artists, albums or songs your friend may ask for, or tick **Share whole library** for everything (including songs you add later). - **Playlists** — tick the playlists to share, or **Share all playlists**. A shared playlist brings its songs along: they show up ticked and greyed in the Library section (hover one to see which playlist is holding it there). **Select All** and **Select None** do what they say — and if you click one by accident, just press **Cancel**: nothing is saved until you press **OK**. Want everyone to see the same thing? On the **General** page, tick **Share the same selection with all friends** and choose there instead. Sharing something doesn't send it. Your friend only receives what they ask for. ### 4. Browse a friend's library and ask for songs Once you have a friend, a small **▾** appears on the right end of the **Library** button: The Library button with the ▾ friend selector on its right Click the **▾** and pick your friend. The button turns light purple and shows their library's name — you're now looking at what they've shared with you: The Library button in friend mode, purple, reading Sam's Library ![Browsing a friend's library](docs/cassette/friend-mode.png) It works just like your own library, except nothing plays (the songs aren't on your computer yet). Instead, every artist, album and song has a little **cassette button**: | Button | What it means | |---|---| | cassette button, not pressed | Not asked for. Click it to ask your friend for this. | | cassette button, pressed | Asked for — this song is on its way to you. Click again to change your mind. | | cassette button, partly pressed | Some of this artist's or album's songs are asked for. Click to ask for all of them. | Pressing the button on an **artist** or **album** asks for every song under it. Along the bottom of the window: - **Hide tracks I have** hides songs you already own. Songs that are *probably* the same as one of yours (same artist and title, different album or length) stay visible in light grey so you can decide. - The **size** of what you've asked for, next to your free space. It turns **orange** if it would leave so little space that Syncthing pauses, and **red** if it won't fit at all. - **Save and Close** sends your list to your friend and takes you back to your own library. **Cancel** forgets the changes. (Leaving any other way with changes asks *"Save changes?"* first.) The songs you asked for arrive in your library **by themselves**, filed by artist and album like anything you'd add yourself, with *"3 songs arrived from Sam."* at the bottom of the window. They come with their names, album info and artwork — but not your friend's play counts or ratings. ### 5. Follow a friend's playlist In a friend's library, their shared playlists are listed on the left, each with a UFO. Click a playlist's **UFO** to follow it: | Icon | What it means | |---|---| | grey outline UFO | Not following. Click the UFO to follow this playlist. | | purple UFO | Following. Click again to stop. | Press **Save and Close**, and the playlist appears in **your** sidebar with a purple UFO, and plays like any other playlist: ![A followed playlist in the sidebar](docs/cassette/followed-playlist.png) - Songs that are still on their way show in grey; they become playable as they arrive. The bottom of the window says when your friend's folder last synced. - A followed playlist's songs are **kept separate from your library** — you get to listen without them filling up your collection. Like one? Right-click the column headings, turn on **Cassette**, and press its cassette button to add it to your library. - Right-click a followed playlist in the sidebar and choose **Unfollow** to let it go (its songs are cleaned up afterwards). Two choices per friend, in **Sync Settings** on their page: - Show followed playlists **mixed in with yours**, or grouped **in a folder with their name**. - **Keep their songs separate** (the default), or **add them to your library** automatically. ### If something isn't working Sync Settings always says what's going on in plain words and what to do next. The common ones: - **"Syncthing isn't running"** — run `systemctl --user start syncthing`, then press **Retry** on the General page. - **"Can't reach Syncthing's API"** — press **Read from Syncthing** on the General page. (Or copy the key from Syncthing's page at : **Actions ▸ Settings ▸ General ▸ API Key**.) - **"Waiting for *Name*'s computer"** for a long time — both computers need to be on with Syncthing running, and your friend needs to have opened LinTunes once since you pasted the code. If it never connects, the code may already have been used: ask for a new one. - **"That code isn't valid"** — part of the code got lost when it was copied. It's one long line starting with `LTC1.`; ask your friend to send it again. - **"This invite has already been used"** — codes work once. Ask for a new one. To stop sharing with someone, open their page in Sync Settings and press **Remove Friend…**. Songs you already brought into your library stay.