refrain 0.2.2__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {refrain-0.2.2 → refrain-0.2.3}/.github/ISSUE_TEMPLATE/bug_report.yml +1 -1
- {refrain-0.2.2 → refrain-0.2.3}/.gitignore +1 -0
- {refrain-0.2.2 → refrain-0.2.3}/CHANGELOG.md +124 -1
- {refrain-0.2.2 → refrain-0.2.3}/PKG-INFO +5 -4
- {refrain-0.2.2 → refrain-0.2.3}/README.md +4 -3
- {refrain-0.2.2 → refrain-0.2.3}/ROADMAP.md +50 -0
- refrain-0.2.3/docs/test-matrix.md +169 -0
- {refrain-0.2.2 → refrain-0.2.3}/packaging/appimage/AppImageBuilder.yml +1 -1
- refrain-0.2.3/packaging/aur/refrain/.SRCINFO +19 -0
- {refrain-0.2.2 → refrain-0.2.3}/packaging/aur/refrain/PKGBUILD +8 -2
- {refrain-0.2.2 → refrain-0.2.3}/packaging/flatpak/io.github.Rockykln.Refrain.metainfo.xml +21 -0
- {refrain-0.2.2 → refrain-0.2.3}/pyproject.toml +1 -1
- refrain-0.2.3/src/refrain/__init__.py +1 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/app.py +163 -9
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/autostart.py +25 -15
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/config.py +14 -1
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/daemon.py +30 -23
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/discord_rpc.py +65 -2
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/logging_setup.py +19 -11
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/sources/bluetooth.py +31 -2
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/sources/mpris.py +10 -2
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/sources/mpris_server.py +11 -3
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/timing.py +36 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/ui/log_window.py +6 -1
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/ui/settings_window.py +17 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/ui/update_dialog.py +54 -4
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/ui/welcome_dialog.py +12 -4
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/updater.py +57 -4
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_autostart.py +28 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_timing.py +47 -1
- refrain-0.2.2/src/refrain/__init__.py +0 -1
- {refrain-0.2.2 → refrain-0.2.3}/.editorconfig +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/.gitattributes +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/.github/FUNDING.yml +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/.github/codeql/codeql-config.yml +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/.github/dependabot.yml +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/.github/pull_request_template.md +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/.github/workflows/codeql.yml +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/.github/workflows/release.yml +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/.github/workflows/security.yml +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/.github/workflows/tests.yml +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/CODE_OF_CONDUCT.md +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/CONTRIBUTING.md +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/LICENSE +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/Makefile +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/SECURITY.md +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/architecture.md +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/faq.md +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/screenshots/README.md +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/screenshots/discord-rpc.png +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/screenshots/live-log.png +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/screenshots/notification.png +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/screenshots/settings-advanced.png +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/screenshots/settings-general.png +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/screenshots/settings-sources.png +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/screenshots/settings-updates.png +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/screenshots/tray-menu.png +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/screenshots/update-dialog.png +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/docs/screenshots/welcome.png +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/packaging/README.md +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/packaging/aur/refrain-git/PKGBUILD +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/packaging/flatpak/io.github.Rockykln.Refrain.desktop +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/packaging/flatpak/io.github.Rockykln.Refrain.yaml +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/packaging/flatpak/python-deps.json +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/requirements-dev.txt +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/requirements.txt +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/__main__.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/assets/icons/github-mark.svg +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/assets/icons/refrain.png +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/assets/icons/refrain.svg +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/assets/icons/tray-paused-dark.svg +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/assets/icons/tray-paused.svg +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/assets/icons/tray-playing-dark.svg +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/assets/icons/tray-playing.svg +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/assets/icons/tray-stopped-dark.svg +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/assets/icons/tray-stopped.svg +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/assets/refrain.desktop +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/cover_art.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/cover_fetcher.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/i18n/refrain_de.qm +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/i18n/refrain_de.ts +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/paths.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/single_instance.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/sources/__init__.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/sources/base.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/ui/__init__.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/src/refrain/ui/tray.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/__init__.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/conftest.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_config.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_cover_art.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_cover_fetcher.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_daemon_album_format.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_daemon_idle.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_discord_listening.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_imports.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_mpris_dispatch.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_paths.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_sources_base.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_tray_init.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_update_dialog_body.py +0 -0
- {refrain-0.2.2 → refrain-0.2.3}/tests/test_updater.py +0 -0
|
@@ -7,6 +7,122 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.3] - 2026-05-07
|
|
11
|
+
|
|
12
|
+
A reliability + portability pass driven by hands-on testing across
|
|
13
|
+
Ubuntu 25.04, CentOS Stream 10, and the existing CachyOS daily driver.
|
|
14
|
+
No breaking changes — same config, same UI, same Python floor; the
|
|
15
|
+
release just makes Refrain behave correctly on more distros and tightens
|
|
16
|
+
a handful of long-standing rough edges.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **Sandboxed Discord IPC bridge** — Snap and Flatpak Discord builds
|
|
21
|
+
publish their `discord-ipc-N` socket inside the sandbox tree instead
|
|
22
|
+
of `$XDG_RUNTIME_DIR`, so pypresence's stock discovery never finds it.
|
|
23
|
+
`_bridge_sandboxed_ipc_socket` probes the three known sandbox paths
|
|
24
|
+
(Flatpak instance dir, Flatpak config-dir layout, Snap path) and
|
|
25
|
+
symlinks the first hit into `$XDG_RUNTIME_DIR` before each connect
|
|
26
|
+
attempt. The welcome-dialog probe walks the same paths so first-run
|
|
27
|
+
diagnostics turn green on those installs.
|
|
28
|
+
- **Cancel button in the update dialog** — `_apply_appimage` now
|
|
29
|
+
reads in 64 KiB chunks and polls a cancellation callback between
|
|
30
|
+
blocks. The dialog repurposes its "Later" button as "Cancel" while a
|
|
31
|
+
download is running, treats window-close as cancel, and removes the
|
|
32
|
+
partial `.AppImage.new` on abort. `UpdateResult.cancelled` lets the
|
|
33
|
+
dialog suppress the failure popup for user-initiated aborts.
|
|
34
|
+
- **Orphan-download self-heal** — `cleanup_orphan_downloads` runs once
|
|
35
|
+
at startup so a `*.AppImage.new` left behind by a SIGKILL or power
|
|
36
|
+
loss mid-download doesn't sit next to the binary forever.
|
|
37
|
+
- **Browser hints expanded** — Floorp, Waterfox, Mullvad Browser, Tor
|
|
38
|
+
Browser, and ungoogled-chromium are now in `DEFAULT_BROWSER_HINTS`
|
|
39
|
+
and surface as Settings checkboxes. Existing configs keep their
|
|
40
|
+
saved list (no auto-migration); the new entries appear unticked
|
|
41
|
+
until the user toggles them.
|
|
42
|
+
- **iTunes-catalog duration as MPRIS override** — when MPRIS reports
|
|
43
|
+
an obviously-wrong `mpris:length` (Apple Music's preview-clip 14 s,
|
|
44
|
+
the playlist-total instead of the track length, or a stale value
|
|
45
|
+
carried over from the previous track), `pick_effective_duration_ms`
|
|
46
|
+
prefers the iTunes value if the two disagree by more than 15 % or
|
|
47
|
+
if MPRIS sits in the preview-clip band on a song iTunes considers
|
|
48
|
+
full-length. 8 new unit tests in `test_timing.py`. Discord no
|
|
49
|
+
longer briefly flips a 2:11 song's total to 0:14 / 7:21 mid-track.
|
|
50
|
+
- **`docs/test-matrix.md`** — eight Tier-1 distros that together
|
|
51
|
+
cover ≈ 95 % of the realistic user base, plus Tier-2 spot-checks,
|
|
52
|
+
Tier-3 desktop / compositor combinations, and a six-step smoke
|
|
53
|
+
check ("tray + theme parity", "settings round-trip", "MPRIS to
|
|
54
|
+
Discord", "Bluetooth", "update-dialog cleanup", "restart cycle")
|
|
55
|
+
per row.
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- **Qt style plugin path augmentation also walks Debian / Ubuntu and
|
|
60
|
+
Fedora / RHEL / openSUSE layouts** — the v0.2.2 fix only checked
|
|
61
|
+
`/usr/lib/qt6/plugins` (Arch). pip / pipx installs on those distros
|
|
62
|
+
now also pick up the system Breeze plugin instead of falling back
|
|
63
|
+
to Fusion. Detection picks the first existing path among
|
|
64
|
+
`/usr/lib/<arch-triple>/qt6/plugins`, `/usr/lib64/qt6/plugins`,
|
|
65
|
+
`/usr/lib/qt6/plugins`.
|
|
66
|
+
- **"No system tray" error** rewritten to cover GNOME, MATE, XFCE,
|
|
67
|
+
Hyprland / Sway / i3 / river, plus a "should work, try logging
|
|
68
|
+
out" hint for Plasma / Cinnamon / LXQt / Budgie. Previously the
|
|
69
|
+
fix advice was GNOME-AppIndicator-only, leaving everyone else to
|
|
70
|
+
guess.
|
|
71
|
+
- **`refrain --install-desktop`** rewrites the installed
|
|
72
|
+
`Exec=` line to point at the actual launcher path of the running
|
|
73
|
+
install. Previously the bundled `Exec=refrain` only worked when
|
|
74
|
+
`refrain` was on `$PATH` (distro packages, pipx); AppImage and
|
|
75
|
+
source-checkout users got a broken menu entry. Resolution order
|
|
76
|
+
matches the autostart logic — extracted into the new
|
|
77
|
+
`autostart.resolve_exec_line(extra_args)` helper.
|
|
78
|
+
- **MPRIS source duration handling decouples drift-resync from the
|
|
79
|
+
payload's preview-clip flag** — the drift-skip still keys off the
|
|
80
|
+
raw MPRIS-reported duration (because the position-field looping is
|
|
81
|
+
a property of MPRIS' preview-clip mode), but the Discord activity
|
|
82
|
+
payload's start/end fields key off the *effective* duration so a
|
|
83
|
+
brief MPRIS preview-clip glitch on a full-length song doesn't kill
|
|
84
|
+
the progress bar.
|
|
85
|
+
- **PyGObject "not installed" warning fires once per startup**
|
|
86
|
+
instead of twice (eager init from `app.py` plus lazy fallback in
|
|
87
|
+
`MPRISServer.start` both used to log). The install hint also lists
|
|
88
|
+
the Fedora / RHEL / openSUSE package name (`python3-gobject`)
|
|
89
|
+
alongside the Arch and Debian ones.
|
|
90
|
+
|
|
91
|
+
### Fixed
|
|
92
|
+
|
|
93
|
+
- **Settings / Log / Update windows now carry the Refrain icon
|
|
94
|
+
explicitly** — on GNOME Wayland the title-based heuristic was
|
|
95
|
+
matching "Settings" against `org.gnome.Settings` and rendering the
|
|
96
|
+
gnome-control-center gear icon in the dock. The WelcomeDialog
|
|
97
|
+
already set its own icon; the other three top-level windows did
|
|
98
|
+
not.
|
|
99
|
+
- **Bluetooth fast-fail when `org.bluez` is not present** — every
|
|
100
|
+
poll cycle on a VM / minimal install previously triggered a
|
|
101
|
+
`service_start_timeout=25000ms` D-Bus activation timeout, blocking
|
|
102
|
+
the worker thread for the full 25 s. `NameHasOwner` returns
|
|
103
|
+
instantly without triggering activation; the three entry points
|
|
104
|
+
(`read`, `_call_method`, `list_paired_devices`) now short-circuit
|
|
105
|
+
when the owner check returns False.
|
|
106
|
+
- **Logging gaps closed in six places** — `setup_logging` runs
|
|
107
|
+
before `Config.load` so the "created default config" /
|
|
108
|
+
"config unreadable" messages reach the file log; toggling the log
|
|
109
|
+
level in Settings now applies to the running root logger via
|
|
110
|
+
`_apply_log_level` on `settings.applied`; the "no system tray"
|
|
111
|
+
early-exit gets a `log.error()`; `os.execvp`-based restarts call
|
|
112
|
+
`logging.shutdown()` first so the file handler's last buffered
|
|
113
|
+
line isn't lost; `Config.load` exception path adds `exc_info=True`;
|
|
114
|
+
`setup_logging` degrades gracefully when the XDG state dir or log
|
|
115
|
+
file can't be opened (console handler always attaches first).
|
|
116
|
+
- **Catch-all exception branches in `DiscordRPC._ensure_connected`,
|
|
117
|
+
`MPRISSource._call_method_on`, and `BluetoothSource._call_method`**
|
|
118
|
+
now use `log.exception()` so the traceback lands in `refrain.log`
|
|
119
|
+
for unexpected errors. Previously they only logged `str(exc)`,
|
|
120
|
+
losing the stack exactly when it would matter most.
|
|
121
|
+
- **Qt-internal log records routed through `refrain.qt`** instead
|
|
122
|
+
of the bare `qt` logger, so they parent under the project
|
|
123
|
+
namespace and show up alongside everything else when the user
|
|
124
|
+
filters by name.
|
|
125
|
+
|
|
10
126
|
## [0.2.2] - 2026-05-06
|
|
11
127
|
|
|
12
128
|
Two roadmap features pulled forward from v0.3 plus a substantial batch
|
|
@@ -568,7 +684,14 @@ with a proper, installable Linux app.
|
|
|
568
684
|
pip-audit, trufflehog, release), Dependabot, issue + PR templates,
|
|
569
685
|
`CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`.
|
|
570
686
|
|
|
571
|
-
[Unreleased]: https://github.com/Rockykln/refrain/compare/v0.
|
|
687
|
+
[Unreleased]: https://github.com/Rockykln/refrain/compare/v0.2.3...HEAD
|
|
688
|
+
[0.2.3]: https://github.com/Rockykln/refrain/compare/v0.2.2...v0.2.3
|
|
689
|
+
[0.2.2]: https://github.com/Rockykln/refrain/compare/v0.2.1...v0.2.2
|
|
690
|
+
[0.2.1]: https://github.com/Rockykln/refrain/compare/v0.2.0...v0.2.1
|
|
691
|
+
[0.2.0]: https://github.com/Rockykln/refrain/compare/v0.1.5...v0.2.0
|
|
692
|
+
[0.1.5]: https://github.com/Rockykln/refrain/compare/v0.1.4...v0.1.5
|
|
693
|
+
[0.1.4]: https://github.com/Rockykln/refrain/compare/v0.1.3...v0.1.4
|
|
694
|
+
[0.1.3]: https://github.com/Rockykln/refrain/compare/v0.1.2...v0.1.3
|
|
572
695
|
[0.1.2]: https://github.com/Rockykln/refrain/compare/v0.1.1...v0.1.2
|
|
573
696
|
[0.1.1]: https://github.com/Rockykln/refrain/compare/v0.1.0...v0.1.1
|
|
574
697
|
[0.1.0]: https://github.com/Rockykln/refrain/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: refrain
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Discord Rich Presence for Apple Music on Linux
|
|
5
5
|
Project-URL: Homepage, https://github.com/Rockykln/refrain
|
|
6
6
|
Project-URL: Repository, https://github.com/Rockykln/refrain
|
|
@@ -134,9 +134,10 @@ and `~/.local/share/icons/`. To undo: `refrain --uninstall-desktop`.
|
|
|
134
134
|
|
|
135
135
|
### Tested on
|
|
136
136
|
|
|
137
|
-
-
|
|
138
|
-
|
|
139
|
-
-
|
|
137
|
+
See [`docs/test-matrix.md`](docs/test-matrix.md) for the full Tier-1 / Tier-2 list, the per-row smoke checks, and which distros are explicitly out-of-scope (Python or glibc floor too low).
|
|
138
|
+
|
|
139
|
+
- **Tier 1 (must pass every release):** CachyOS, Arch Linux, Fedora 42, Ubuntu 24.04 LTS, Debian 13, openSUSE Tumbleweed, Linux Mint 22, Manjaro Stable.
|
|
140
|
+
- **Desktops:** KDE Plasma 6 (Wayland is the primary target, X11 also covered), GNOME with the [AppIndicator and KStatusNotifierItem](https://extensions.gnome.org/extension/615/appindicator-support/) extension, XFCE / Cinnamon / LXQt / Budgie via their native or AppIndicator-bridged tray, MATE with `mate-applet-statusnotifier`, tiling WMs (Hyprland / Sway / i3 / river) via a SNI-capable status bar.
|
|
140
141
|
|
|
141
142
|
## First-time setup
|
|
142
143
|
|
|
@@ -100,9 +100,10 @@ and `~/.local/share/icons/`. To undo: `refrain --uninstall-desktop`.
|
|
|
100
100
|
|
|
101
101
|
### Tested on
|
|
102
102
|
|
|
103
|
-
-
|
|
104
|
-
|
|
105
|
-
-
|
|
103
|
+
See [`docs/test-matrix.md`](docs/test-matrix.md) for the full Tier-1 / Tier-2 list, the per-row smoke checks, and which distros are explicitly out-of-scope (Python or glibc floor too low).
|
|
104
|
+
|
|
105
|
+
- **Tier 1 (must pass every release):** CachyOS, Arch Linux, Fedora 42, Ubuntu 24.04 LTS, Debian 13, openSUSE Tumbleweed, Linux Mint 22, Manjaro Stable.
|
|
106
|
+
- **Desktops:** KDE Plasma 6 (Wayland is the primary target, X11 also covered), GNOME with the [AppIndicator and KStatusNotifierItem](https://extensions.gnome.org/extension/615/appindicator-support/) extension, XFCE / Cinnamon / LXQt / Budgie via their native or AppIndicator-bridged tray, MATE with `mate-applet-statusnotifier`, tiling WMs (Hyprland / Sway / i3 / river) via a SNI-capable status bar.
|
|
106
107
|
|
|
107
108
|
## First-time setup
|
|
108
109
|
|
|
@@ -169,6 +169,56 @@ What's done, what's next, what's deliberately not in scope.
|
|
|
169
169
|
in the wheel**, **MPRIS dispatch logging at INFO** so
|
|
170
170
|
Next/Previous routing is visible in the live log.
|
|
171
171
|
|
|
172
|
+
## Done — v0.2.3
|
|
173
|
+
|
|
174
|
+
- **Distro-portability sweep** driven by hands-on testing.
|
|
175
|
+
- Qt style plugin path augmentation extended to Debian / Ubuntu
|
|
176
|
+
(`/usr/lib/<arch-triple>/qt6/plugins`) and Fedora / RHEL / openSUSE
|
|
177
|
+
(`/usr/lib64/qt6/plugins`); v0.2.2 only handled the Arch layout.
|
|
178
|
+
- Snap and Flatpak Discord builds now reachable out of the box —
|
|
179
|
+
`_bridge_sandboxed_ipc_socket` symlinks the sandboxed
|
|
180
|
+
`discord-ipc-N` into `$XDG_RUNTIME_DIR` before each pypresence
|
|
181
|
+
connect attempt.
|
|
182
|
+
- Bluetooth fast-fail via `NameHasOwner('org.bluez')` so VMs and
|
|
183
|
+
minimal installs without bluez no longer block the daemon's
|
|
184
|
+
poll cycle for 25 s waiting on D-Bus service activation.
|
|
185
|
+
- "No system tray" error covers GNOME, MATE, XFCE, and the
|
|
186
|
+
tiling-WM crowd (Hyprland / Sway / i3 / river); was
|
|
187
|
+
GNOME-AppIndicator-only.
|
|
188
|
+
- `--install-desktop` rewrites `Exec=` in the installed `.desktop`
|
|
189
|
+
to match the running install (AppImage path / venv shim / system
|
|
190
|
+
binary), so AppImage and source-checkout users get a working
|
|
191
|
+
menu entry.
|
|
192
|
+
- Browser hints expanded to cover Floorp, Waterfox, Mullvad
|
|
193
|
+
Browser, Tor Browser, ungoogled-chromium.
|
|
194
|
+
- **iTunes-catalog duration as MPRIS override** —
|
|
195
|
+
`pick_effective_duration_ms` chooses iTunes when MPRIS and iTunes
|
|
196
|
+
disagree by more than 15 % or when MPRIS sits in the preview-clip
|
|
197
|
+
band on a song iTunes considers full-length. Apple Music's MPRIS
|
|
198
|
+
surface no longer makes Discord briefly flip a 2:11 song's total
|
|
199
|
+
to 0:14 / 7:21 mid-track.
|
|
200
|
+
- **Update-dialog Cancel + orphan-download self-heal** — `Cancel`
|
|
201
|
+
button + window-close handler abort an in-flight AppImage
|
|
202
|
+
download cleanly and remove the partial `.new`. A separate
|
|
203
|
+
`cleanup_orphan_downloads` runs at startup so a SIGKILL or power
|
|
204
|
+
loss mid-download doesn't leave a stale `*.AppImage.new` next to
|
|
205
|
+
the binary forever.
|
|
206
|
+
- **Per-window icons** on Settings / Log / Update dialogs so GNOME
|
|
207
|
+
Shell's title-based heuristic on Wayland doesn't render them with
|
|
208
|
+
the gnome-control-center gear icon in the dock.
|
|
209
|
+
- **Logging audit** closed six gaps: `setup_logging` runs before
|
|
210
|
+
`Config.load` so config-load messages reach the file log; the
|
|
211
|
+
log-level toggle in Settings applies live; `setup_logging` degrades
|
|
212
|
+
gracefully when the XDG state dir is unwritable; `os.execvp`-based
|
|
213
|
+
restarts flush log handlers first; catch-all exception branches in
|
|
214
|
+
`DiscordRPC._ensure_connected`, `MPRISSource._call_method_on` and
|
|
215
|
+
`BluetoothSource._call_method` switched to `log.exception()` so
|
|
216
|
+
tracebacks land in `refrain.log`.
|
|
217
|
+
- **`docs/test-matrix.md`** — eight Tier-1 distros, Tier-2 spot-check
|
|
218
|
+
list, Tier-3 desktop / compositor sweep, six-step smoke checks,
|
|
219
|
+
out-of-scope reasons for the floor of unsupported distros (RHEL 9
|
|
220
|
+
/ Rocky 9 / Alma 9 / Debian 11 / Ubuntu 22.04 / Alpine).
|
|
221
|
+
|
|
172
222
|
## Up next — v0.3
|
|
173
223
|
|
|
174
224
|
- **Polishing the wrapped i18n surface** — wrap the remaining
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Test matrix
|
|
2
|
+
|
|
3
|
+
The set of distros + desktop combinations Refrain is expected to work on,
|
|
4
|
+
and a six-step smoke test to run on each before declaring a release good
|
|
5
|
+
to ship. Tick boxes as you verify; leave a date + Refrain version in the
|
|
6
|
+
"Last verified" column.
|
|
7
|
+
|
|
8
|
+
## How to use this document
|
|
9
|
+
|
|
10
|
+
For each row in the matrix:
|
|
11
|
+
|
|
12
|
+
1. Run through the six checks under [Smoke checks](#smoke-checks).
|
|
13
|
+
2. If everything passes, tick the row's checkbox and write the date +
|
|
14
|
+
Refrain version into "Last verified".
|
|
15
|
+
3. If something fails, file an issue and leave the row unticked. Note
|
|
16
|
+
the failing check in "Notes".
|
|
17
|
+
|
|
18
|
+
A row failing one minor optional check (e.g. notifications missing
|
|
19
|
+
because `notify-send` isn't installed) is fine — that's documented
|
|
20
|
+
graceful-degradation, not a regression.
|
|
21
|
+
|
|
22
|
+
## Tier 1 — primary support, must pass every release
|
|
23
|
+
|
|
24
|
+
These are the eight systems that together cover ≈ 95 % of the realistic
|
|
25
|
+
user base. CachyOS / KDE Plasma 6 / Wayland is the maintainer's daily
|
|
26
|
+
driver and should be tested on every commit; the rest before each tag.
|
|
27
|
+
|
|
28
|
+
| ✓ | Distro | Desktop | Display server | Channel | Last verified | Notes |
|
|
29
|
+
|---|---|---|---|---|---|---|
|
|
30
|
+
| [ ] | **CachyOS** (rolling) | KDE Plasma 6 | Wayland | AUR `refrain` | | maintainer daily |
|
|
31
|
+
| [ ] | **Arch Linux** (rolling) | KDE Plasma 6 | X11 | AUR `refrain` | | alternate display server |
|
|
32
|
+
| [ ] | **Fedora 42 Workstation** | GNOME 47 | Wayland | AppImage + PyPI | | RPM world + GNOME tray ext. |
|
|
33
|
+
| [ ] | **Ubuntu 24.04 LTS** | GNOME 46 | Wayland | AppImage | | LTS, glibc floor |
|
|
34
|
+
| ❌ | **Ubuntu 25.04** | GNOME 48 | Wayland | PyPI | 2026-05-07 / v0.2.2 | Tested + **failed**. Settings window came up with the gnome-control-center icon (Wayland app-id matching heuristic; fixed in `1e759ba` by setting per-window icon explicitly). Snap-sandboxed Firefox + Chrome both refused to publish MPRIS so Refrain saw no players — distro-external, workaround is a deb-channel browser (Brave / Mozilla apt repo). 25 s `org.bluez` activation timeout per poll when bluez isn't present (fast-fail via `NameHasOwner` added in follow-up). Re-test with v0.2.3+ once those fixes ship. |
|
|
35
|
+
| [ ] | **Debian 13** (Trixie) | KDE Plasma 6 | Wayland | AppImage | | Plasma outside Arch |
|
|
36
|
+
| [ ] | **openSUSE Tumbleweed** | KDE Plasma 6 | Wayland | PyPI in venv | | rolling non-Arch |
|
|
37
|
+
| [ ] | **Linux Mint 22** | Cinnamon 6 | X11 | AppImage | | Cinnamon tray, X11 |
|
|
38
|
+
| [ ] | **Manjaro** Stable | KDE Plasma 6 | Wayland | AUR `refrain` | | delayed Arch mirror |
|
|
39
|
+
|
|
40
|
+
## Tier 2 — supported, spot-check
|
|
41
|
+
|
|
42
|
+
Less frequent verification — once per minor release is enough.
|
|
43
|
+
|
|
44
|
+
| ✓ | Distro | Desktop | Channel | Notes |
|
|
45
|
+
|---|---|---|---|---|
|
|
46
|
+
| [ ] | **EndeavourOS** | KDE | AUR `refrain` | Arch derivative |
|
|
47
|
+
| [ ] | **Garuda Linux** | KDE | AUR `refrain` | Arch derivative |
|
|
48
|
+
| [ ] | **Pop!_OS 24.04** | GNOME (or COSMIC) | AppImage | COSMIC tray TBD |
|
|
49
|
+
| [ ] | **KDE Neon** (User) | KDE Plasma 6 | PyPI / AppImage | Plasma testing target |
|
|
50
|
+
| [ ] | **Ubuntu 25.04** | GNOME | PyPI in venv | Python 3.13 |
|
|
51
|
+
| [ ] | **Debian 12** (Bookworm) | KDE / GNOME | AppImage | Python 3.11, Qt 5 — AppImage required |
|
|
52
|
+
| [ ] | **Fedora 41 KDE Spin** | KDE Plasma 6 | Wayland | AppImage / PyPI | Fedora KDE flavour |
|
|
53
|
+
| [ ] | **NixOS** unstable | KDE / GNOME | PyPI in nix-shell | flake.nix would be nice-to-have |
|
|
54
|
+
|
|
55
|
+
## Tier 3 — desktop / compositor edge cases
|
|
56
|
+
|
|
57
|
+
Not full distro entries — just verify the tray + tray icon rendering
|
|
58
|
+
on these specifically.
|
|
59
|
+
|
|
60
|
+
| ✓ | Compositor / DE | Tray status | Notes |
|
|
61
|
+
|---|---|---|---|
|
|
62
|
+
| [ ] | KDE Plasma 6 — Wayland | native ✓ | primary target |
|
|
63
|
+
| [ ] | KDE Plasma 6 — X11 | native ✓ | |
|
|
64
|
+
| [ ] | GNOME 45+ | needs [AppIndicator extension](https://extensions.gnome.org/extension/615/appindicator-support/) | refuses start without it |
|
|
65
|
+
| [ ] | XFCE 4.18+ | OK with `xfce4-statusnotifier-plugin` | |
|
|
66
|
+
| [ ] | Cinnamon 6 | native ✓ | |
|
|
67
|
+
| [ ] | LXQt 2 | native ✓ | |
|
|
68
|
+
| [ ] | Budgie | OK via AppIndicator | |
|
|
69
|
+
| [ ] | MATE | needs `mate-applet-statusnotifier` | |
|
|
70
|
+
| [ ] | Hyprland / Sway / i3 | needs SNI-capable bar (waybar `tray` module, polybar) | |
|
|
71
|
+
| [ ] | Pantheon (elementary) | unofficial SNI support | untested |
|
|
72
|
+
| [ ] | COSMIC (Pop!_OS 24.04+) | own protocol | untested |
|
|
73
|
+
|
|
74
|
+
## Out of scope (won't work)
|
|
75
|
+
|
|
76
|
+
These are documented as **unsupported** — don't open issues for them.
|
|
77
|
+
|
|
78
|
+
| Distro | Reason |
|
|
79
|
+
|---|---|
|
|
80
|
+
| Debian 11 (Bullseye) | Python 3.9 — too old |
|
|
81
|
+
| Ubuntu 20.04 / 22.04 LTS | Python 3.8 / 3.10 too old; AppImage glibc floor too high for 22.04 |
|
|
82
|
+
| Linux Mint 21.x | Python 3.10 — too old |
|
|
83
|
+
| **CentOS Stream 10** | Tested 2026-05-07 against v0.2.2 — **failed**. AppImage in mounted mode needs `fuse` (not just `fuse-libs`) which the docs didn't surface; in `--appimage-extract-and-run` mode raises `No module named refrain` on a fresh extract. PyPI install starts but GNOME default ships no AppIndicator package out of the box — Refrain refuses to launch with "No system tray". Reachable only with manual Extension-Manager dance. Not Tier-1/2 worth. |
|
|
84
|
+
| CentOS Stream 9 | glibc 2.34 < 2.35 (AppImage breaks); Python 3.9 default |
|
|
85
|
+
| RHEL 9 / Rocky 9 / AlmaLinux 9 | same family as CentOS Stream 9 |
|
|
86
|
+
| RHEL 8 / Rocky 8 / AlmaLinux 8 | glibc 2.28; Python 3.6 default |
|
|
87
|
+
| Alpine Linux | musl libc — PySide6 wheels are glibc-only |
|
|
88
|
+
| openSUSE Leap 15.5 | Python 3.6 default |
|
|
89
|
+
|
|
90
|
+
## Smoke checks
|
|
91
|
+
|
|
92
|
+
Run all six on every Tier 1 row, in order. Each should take under a
|
|
93
|
+
minute; the whole sweep is ≈ 5 minutes per system.
|
|
94
|
+
|
|
95
|
+
### 1. Tray + theme parity
|
|
96
|
+
|
|
97
|
+
- Refrain icon appears in the system tray on launch.
|
|
98
|
+
- Icon style matches the system theme (light glyph on dark panel,
|
|
99
|
+
dark glyph on light panel).
|
|
100
|
+
- Settings window styling matches the rest of the desktop (Breeze
|
|
101
|
+
on KDE, Adwaita-via-Qt on GNOME, etc.). The pip / pipx / AppImage
|
|
102
|
+
builds should look the same as the AUR / system build because of
|
|
103
|
+
the Qt-plugin-path augmentation in `app.py`.
|
|
104
|
+
|
|
105
|
+
### 2. Settings round-trip
|
|
106
|
+
|
|
107
|
+
- Tray → *Settings…* opens the window.
|
|
108
|
+
- Toggle any setting (e.g. *Notifications*), hit *Apply*.
|
|
109
|
+
- Window closes; setting persists in `~/.config/refrain/config.toml`.
|
|
110
|
+
- No console errors, no Qt crashes.
|
|
111
|
+
|
|
112
|
+
### 3. MPRIS / Apple Music — Discord push
|
|
113
|
+
|
|
114
|
+
- Open `https://music.apple.com` in any major Linux browser.
|
|
115
|
+
- Start playing a track.
|
|
116
|
+
- Within ≈ 1 s, the tray menu shows the track title + artist.
|
|
117
|
+
- Within ≈ 2 s, Discord status shows "Listening to ..." with the
|
|
118
|
+
cover art.
|
|
119
|
+
- **Duration check**: progress bar in Discord shows the *correct*
|
|
120
|
+
total length for the track (e.g. a 2:11 song shows 2:11, not 0:14
|
|
121
|
+
or 7:21).
|
|
122
|
+
|
|
123
|
+
### 4. Bluetooth source (optional, hardware-dependent)
|
|
124
|
+
|
|
125
|
+
Skip if no AVRCP-capable Bluetooth source is available. Otherwise:
|
|
126
|
+
|
|
127
|
+
- Pair + connect a phone playing music via Bluetooth.
|
|
128
|
+
- Tray menu shows the track within ≈ 1 s.
|
|
129
|
+
- Discord status pushes the metadata.
|
|
130
|
+
- Per-source `client_id_bluetooth` (if set) routes to the right
|
|
131
|
+
Discord application.
|
|
132
|
+
|
|
133
|
+
### 5. Update dialog cleanup
|
|
134
|
+
|
|
135
|
+
Triggered automatically when a newer version exists; or set
|
|
136
|
+
`update.last_check_ts = 0` in the config and restart.
|
|
137
|
+
|
|
138
|
+
- Update dialog opens with a working *Cancel* button while
|
|
139
|
+
downloading.
|
|
140
|
+
- Hitting Cancel mid-download removes the `*.AppImage.new` file
|
|
141
|
+
next to the binary (only relevant for AppImage installs).
|
|
142
|
+
- Closing the dialog window via the X button while downloading
|
|
143
|
+
also cancels cleanly.
|
|
144
|
+
- After cancel, no orphan `.new` file is left behind.
|
|
145
|
+
|
|
146
|
+
### 6. Restart cycle
|
|
147
|
+
|
|
148
|
+
- Tray → *Restart Refrain*.
|
|
149
|
+
- The process exits cleanly, releases the D-Bus name
|
|
150
|
+
`io.github.Rockykln.Refrain`, and re-execs itself.
|
|
151
|
+
- Tray icon disappears and reappears within ≈ 2 s.
|
|
152
|
+
- Discord RPC reconnects and the same track shows up again.
|
|
153
|
+
- `refrain.log` has a `Re-execing for restart` line followed by a
|
|
154
|
+
fresh `Refrain ... starting` line — no duplicate D-Bus name
|
|
155
|
+
errors.
|
|
156
|
+
|
|
157
|
+
## Failure-mode reference
|
|
158
|
+
|
|
159
|
+
If a check fails, the most common causes:
|
|
160
|
+
|
|
161
|
+
| Symptom | Likely cause |
|
|
162
|
+
|---|---|
|
|
163
|
+
| Refrain refuses to start with "No system tray" | Missing AppIndicator extension on GNOME, or bar with no tray on tiling WM |
|
|
164
|
+
| Tray icon visible but no track shown when playing in browser | `plasma-browser-integration` not enabled in browser, or `mpris_enabled = false` |
|
|
165
|
+
| Discord status never appears | No `client_id` set, or Discord IPC socket not reachable (Snap/Flatpak Discord can hide it) |
|
|
166
|
+
| Cover art missing | iTunes Search has no match for that artist / title — fallback brand icon shown |
|
|
167
|
+
| Settings window has no icons / wrong style on pip/pipx install | Qt plugin-path augmentation didn't fire — check log for `Augmenting Qt plugin path` |
|
|
168
|
+
| Duration shows as 0:14 or some too-short value | Pre-`pick_effective_duration_ms` build, or iTunes lookup hasn't resolved yet for that track |
|
|
169
|
+
| AppImage fails with `failed to exec fusermount` (RHEL family) | `fuse-libs` alone is not enough — install the `fuse` package too, or run with `--appimage-extract-and-run` |
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
pkgbase = refrain
|
|
2
|
+
pkgdesc = Discord Rich Presence for Apple Music on Linux
|
|
3
|
+
pkgver = 0.2.2
|
|
4
|
+
pkgrel = 1
|
|
5
|
+
url = https://github.com/Rockykln/refrain
|
|
6
|
+
arch = any
|
|
7
|
+
license = custom:Refrain
|
|
8
|
+
makedepends = python-build
|
|
9
|
+
makedepends = python-installer
|
|
10
|
+
makedepends = python-hatchling
|
|
11
|
+
makedepends = python-wheel
|
|
12
|
+
depends = python>=3.11
|
|
13
|
+
depends = python-pypresence
|
|
14
|
+
depends = python-dbus
|
|
15
|
+
depends = pyside6
|
|
16
|
+
source = refrain-0.2.2.tar.gz::https://github.com/Rockykln/refrain/archive/v0.2.2.tar.gz
|
|
17
|
+
sha256sums = db07e0783b3f44a7c54d4b8ee2ebe33f3dc577707a03bd42f5958ddae3d5ac12
|
|
18
|
+
|
|
19
|
+
pkgname = refrain
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Maintainer: Rockykln <contact@rockykln.com>
|
|
2
2
|
|
|
3
3
|
pkgname=refrain
|
|
4
|
-
pkgver=0.2.
|
|
4
|
+
pkgver=0.2.3
|
|
5
5
|
pkgrel=1
|
|
6
6
|
pkgdesc="Discord Rich Presence for Apple Music on Linux"
|
|
7
7
|
arch=('any')
|
|
@@ -13,6 +13,9 @@ depends=(
|
|
|
13
13
|
'python-dbus'
|
|
14
14
|
'pyside6'
|
|
15
15
|
)
|
|
16
|
+
optdepends=(
|
|
17
|
+
'python-gobject: enables MPRIS-server publication so Plasma media controls reach Refrain'
|
|
18
|
+
)
|
|
16
19
|
makedepends=(
|
|
17
20
|
'python-build'
|
|
18
21
|
'python-installer'
|
|
@@ -20,7 +23,10 @@ makedepends=(
|
|
|
20
23
|
'python-wheel'
|
|
21
24
|
)
|
|
22
25
|
source=("$pkgname-$pkgver.tar.gz::https://github.com/Rockykln/refrain/archive/v$pkgver.tar.gz")
|
|
23
|
-
|
|
26
|
+
# Placeholder — recompute after the GitHub release exists:
|
|
27
|
+
# curl -sLO "https://github.com/Rockykln/refrain/archive/v${pkgver}.tar.gz"
|
|
28
|
+
# sha256sum "v${pkgver}.tar.gz"
|
|
29
|
+
sha256sums=('SKIP')
|
|
24
30
|
|
|
25
31
|
build() {
|
|
26
32
|
cd "$pkgname-$pkgver"
|
|
@@ -51,6 +51,27 @@
|
|
|
51
51
|
<content_rating type="oars-1.1" />
|
|
52
52
|
|
|
53
53
|
<releases>
|
|
54
|
+
<release version="0.2.3" date="2026-05-07">
|
|
55
|
+
<description>
|
|
56
|
+
<p>
|
|
57
|
+
Reliability + portability pass. Sandbox-aware Discord IPC
|
|
58
|
+
discovery so Snap and Flatpak Discord builds are reachable
|
|
59
|
+
out of the box, Qt style plugin path augmentation extended
|
|
60
|
+
to Debian / Ubuntu / Fedora / RHEL / openSUSE layouts so
|
|
61
|
+
pip / pipx installs render in the system theme on those
|
|
62
|
+
distros (was Arch-only in 0.2.2), Bluetooth fast-fail so
|
|
63
|
+
VMs without bluez don't stall the poll cycle for 25 s, and
|
|
64
|
+
"No system tray" advice extended past the GNOME
|
|
65
|
+
AppIndicator hint to cover MATE, XFCE, and tiling
|
|
66
|
+
window managers. Plus a Cancel button on the update
|
|
67
|
+
dialog, orphan-download self-heal, per-window icons (no
|
|
68
|
+
more gnome-control-center gear icon on Wayland), an
|
|
69
|
+
iTunes-catalog duration override that fixes Discord briefly
|
|
70
|
+
flipping a 2:11 song's total to 0:14 mid-track, and a
|
|
71
|
+
six-step distro test matrix in docs/test-matrix.md.
|
|
72
|
+
</p>
|
|
73
|
+
</description>
|
|
74
|
+
</release>
|
|
54
75
|
<release version="0.2.2" date="2026-05-06">
|
|
55
76
|
<description>
|
|
56
77
|
<p>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.3"
|