Discord Rich Presence for Apple Music on Linux
Project description
Refrain
Discord Rich Presence for Apple Music on Linux.
Refrain shows what you're listening to on Apple Music as your Discord status
— whether the audio is playing in a browser tab on music.apple.com or
streaming from your iPhone over Bluetooth.
What it does
- Reads playback metadata from MPRIS (Apple Music in any major Linux browser) and BlueZ AVRCP (any AVRCP-capable Bluetooth source).
- Forwards track + cover art to Discord via the local IPC socket.
- Lives in your system tray with Play/Pause/Next/Previous controls.
- Provides a settings window (PySide6) for everything users typically want to tweak — privacy mode, sources, autostart, Bluetooth device picker.
┌─────────────────────────────────────────────────────────┐
│ Refrain │
│ │
│ ┌──────────┐ ┌──────────┐ ┌────────────────┐ │
│ │ MPRIS │ │ BlueZ │ │ Tray + │ │
│ │ source │ │ AVRCP │ │ Settings UI │ │
│ └────┬─────┘ └────┬─────┘ └───────┬────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Background daemon │ │
│ └────────────────────┬────────────────────────┘ │
│ │ │
│ ▼ │
│ Discord Rich Presence (IPC) │
└─────────────────────────────────────────────────────────┘
Requirements
- Linux with D-Bus
- Python ≥ 3.11
- Discord desktop client running (Refrain talks to its IPC socket)
- For Bluetooth: BlueZ with AVRCP enabled
Install
| Channel | Install |
|---|---|
| PyPI (any distro with Python ≥ 3.11) | pip install refrain |
| AUR (Arch / CachyOS / Manjaro / EndeavourOS) | yay -S refrain (stable) or yay -S refrain-git (latest main) |
| AppImage (portable single-file, any glibc-based distro) | Download from the Releases page |
| Flatpak | flatpak install flathub io.github.Rockykln.Refrain (submission pending review) |
| From source | See below |
Build files for all four channels live under packaging/.
See packaging/README.md for build instructions.
From source (development)
git clone https://github.com/Rockykln/refrain.git
cd refrain
# On distros that already package Qt-for-Python and dbus-python (Arch, Fedora,
# openSUSE …) a venv with --system-site-packages avoids re-downloading them.
python -m venv --system-site-packages .venv
source .venv/bin/activate
pip install -e .
refrain
If your distro doesn't ship PySide6 or dbus-python, plain
python -m venv .venv works too — pip will pull PySide6 from PyPI and
build dbus-python against your system's D-Bus headers
(libdbus-1-dev on Debian/Ubuntu, dbus-devel on Fedora).
Pip-installed users: get a launcher
When installed via pip rather than a distro package, Refrain doesn't
register itself with your application menu. Run once after install:
refrain --install-desktop
This copies the .desktop file and icon to ~/.local/share/applications/
and ~/.local/share/icons/. To undo: refrain --uninstall-desktop.
Tested on
- KDE Plasma 6 (Wayland) — primary target
- KDE Plasma 6 (X11)
- GNOME (with the AppIndicator and KStatusNotifierItem extension)
First-time setup
Refrain needs a Discord Application ID to push status updates. Each user registers their own (free, takes 30 seconds):
- Open https://discord.com/developers/applications and click New Application.
- Name it whatever you want — that name is what shows up under "Listening to ..." in your Discord status. You can also upload a square image as the application icon; Discord uses it as the fallback when there's no album cover.
- Copy the Application ID from the General Information page.
- Launch Refrain → Settings → General → Discord Client ID → paste, Apply.
That's it. The status will appear in Discord on the next track change.
Configuration
Settings live at $XDG_CONFIG_HOME/refrain/config.toml
(typically ~/.config/refrain/config.toml). The settings window edits the
same file; you almost never need to touch it by hand.
[discord]
client_id = "" # paste your own Application ID here
[sources]
mpris_enabled = true
bluetooth_enabled = true
bluetooth_device = "" # empty = auto-detect, or "AA:BB:CC:DD:EE:FF"
[privacy]
mode = "full" # "full" | "minimal" | "off"
[behavior]
autostart = false
notifications = true
cover_art = true
show_buttons = true
[advanced]
poll_interval_ms = 1000
log_level = "INFO"
Tray
| Item | What it does |
|---|---|
| ♪ Title | Currently playing track (read-only) |
| Artist • Album | Currently playing artist + album (read-only) |
| ⏱ X:XX / Y:YY | Elapsed time / track length (and remaining) |
| ⏮ Previous | Skip backward on the active source |
| ⏵ Play / ⏸ Pause | Toggle on the active source (label follows playback state) |
| ⏭ Next | Skip forward on the active source |
| ⬆ Update available | Only visible when a newer release exists |
| Settings… | Open the settings window |
| Live log… | Open the live-log window |
| ⟳ Restart Refrain | Cleanly stop and re-launch (release D-Bus name + RPC, exec the same binary) |
| Quit Refrain | Stop the daemon and exit |
Settings
The settings window opens on first launch, and again any time you click
Settings… from the tray. Hitting Apply writes the change to
config.toml, hides the window, and keeps the daemon + tray running in
the background.
|
General Discord client ID, privacy, autostart, notifications, cover art |
Sources MPRIS / Bluetooth toggles + paired-device picker |
|
Updates Auto-check, last-checked, manual Check for updates now |
Advanced Poll interval, notification delay, cover cache size, log level, live-log, restart |
Notifications
When a track changes, Refrain fires a desktop notification with the album cover, song title, artist and album — the same data that's going to your Discord status. Toggle off in Settings → General if you don't want them.
Updates
Refrain checks the GitHub Releases API once per day on startup. When a newer version exists, the tray menu shows an Update available item that opens this dialog:
Behavior is install-type-aware:
- AppImage — Refrain downloads the new
.AppImagefrom the release assets and replaces the running binary in place (atomic rename), then prompts a restart. - pip / venv — runs
pip install --upgrade refrainfor you. - Flatpak / AUR — never modifies system files; surfaces the distro's
own upgrade command (
flatpak update …/yay -Syu refrain) so the package manager stays in charge.
File locations
| What | Where |
|---|---|
| Config | $XDG_CONFIG_HOME/refrain/config.toml |
| Logs | $XDG_STATE_HOME/refrain/refrain.log (rotates) |
| Cover cache | $XDG_CACHE_HOME/refrain/covers/*.txt |
| Autostart | $XDG_CONFIG_HOME/autostart/refrain.desktop (when enabled) |
Diagnostics — live log
Tray menu → Live log… (or launch with refrain --debug) opens a
streaming view of every log line as it happens, color-coded by level and
filterable. Same content as ~/.local/state/refrain/refrain.log, but
without tailing it from a terminal.
Privacy
The cover-art lookup hits Apple's public iTunes Search API over HTTPS with just the artist + track name. Nothing else leaves your machine — track metadata goes to Discord's local IPC socket, not over the network.
Off privacy mode disables the Discord status entirely while keeping the
tray + controls running.
Documentation
- Architecture overview — threads, D-Bus surface, file paths
- FAQ
- Roadmap
- Changelog
- Contributing — dev setup, testing, code style
- Security policy
- Packaging guide — AUR, Flatpak, AppImage build steps
Contributing
See CONTRIBUTING.md for dev setup, testing, and the
source/UI architecture. PRs welcome — especially for distribution packaging
(Flatpak, AUR, AppImage) and for additional Bluetooth device shapes.
Contact
- General questions, feedback, feature ideas → contact@rockykln.com
- Security reports → report@rockykln.com (also: GitHub private advisory)
- Bugs → please use the issue tracker
License
Refrain License (Use-Only) — see LICENSE.
Refrain is source-available but not open source. In short:
- ✅ Anyone may use, copy, and redistribute the unmodified Software.
- ✅ Anyone may read, study, and reference the source code.
- ❌ Modifications and derivative works (including forks) may not be redistributed.
- ❌ The "Refrain" name and logo may not be used to imply endorsement of or affiliation with modified versions.
Third-party dependencies (PySide6, pypresence, dbus-python) retain
their original licenses (LGPL / MIT).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file refrain-0.1.5.tar.gz.
File metadata
- Download URL: refrain-0.1.5.tar.gz
- Upload date:
- Size: 945.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fbb41417a6b22e28b84503213671343c04f3eb188882335816983192590cb6c2
|
|
| MD5 |
c4fbf8714aaf86c72c3026faf863b3d2
|
|
| BLAKE2b-256 |
2244c0312e306ad3e0dffe2e83d9ce424aec2137a0415c3d7e4d7c55c627317b
|
Provenance
The following attestation bundles were made for refrain-0.1.5.tar.gz:
Publisher:
release.yml on Rockykln/refrain
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
refrain-0.1.5.tar.gz -
Subject digest:
fbb41417a6b22e28b84503213671343c04f3eb188882335816983192590cb6c2 - Sigstore transparency entry: 1448789578
- Sigstore integration time:
-
Permalink:
Rockykln/refrain@f72d7c153bc486a8c6ba32311e2eba2574c16651 -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/Rockykln
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f72d7c153bc486a8c6ba32311e2eba2574c16651 -
Trigger Event:
push
-
Statement type:
File details
Details for the file refrain-0.1.5-py3-none-any.whl.
File metadata
- Download URL: refrain-0.1.5-py3-none-any.whl
- Upload date:
- Size: 51.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d3b04cf5653f17cb3189c3cca2a3dcf1d681af80449a942c7f58342611f3c0d
|
|
| MD5 |
818c49a287511b6fb1eb7290bc8abaf7
|
|
| BLAKE2b-256 |
c31901a6a0ed03509c1f3d34482ab6e53fd79723676bdd84417488eb78f50104
|
Provenance
The following attestation bundles were made for refrain-0.1.5-py3-none-any.whl:
Publisher:
release.yml on Rockykln/refrain
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
refrain-0.1.5-py3-none-any.whl -
Subject digest:
7d3b04cf5653f17cb3189c3cca2a3dcf1d681af80449a942c7f58342611f3c0d - Sigstore transparency entry: 1448789752
- Sigstore integration time:
-
Permalink:
Rockykln/refrain@f72d7c153bc486a8c6ba32311e2eba2574c16651 -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/Rockykln
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f72d7c153bc486a8c6ba32311e2eba2574c16651 -
Trigger Event:
push
-
Statement type: