refrain 0.2.5__tar.gz → 0.2.7__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.5 → refrain-0.2.7}/.github/workflows/release.yml +29 -1
- {refrain-0.2.5 → refrain-0.2.7}/CHANGELOG.md +229 -2
- {refrain-0.2.5 → refrain-0.2.7}/PKG-INFO +11 -2
- {refrain-0.2.5 → refrain-0.2.7}/README.md +10 -1
- {refrain-0.2.5 → refrain-0.2.7}/ROADMAP.md +88 -5
- {refrain-0.2.5 → refrain-0.2.7}/docs/screenshots/README.md +4 -2
- refrain-0.2.7/docs/screenshots/live-log.png +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/docs/screenshots/settings-advanced.png +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/docs/screenshots/settings-general.png +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/docs/screenshots/settings-sources.png +0 -0
- refrain-0.2.7/docs/screenshots/settings-updates.png +0 -0
- refrain-0.2.7/docs/screenshots/tray-menu.png +0 -0
- refrain-0.2.7/docs/screenshots/update-dialog.png +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/packaging/appimage/AppImageBuilder.yml +1 -1
- {refrain-0.2.5 → refrain-0.2.7}/packaging/aur/refrain/.SRCINFO +3 -3
- {refrain-0.2.5 → refrain-0.2.7}/packaging/aur/refrain/PKGBUILD +2 -2
- {refrain-0.2.5 → refrain-0.2.7}/packaging/flatpak/io.github.Rockykln.Refrain.metainfo.xml +32 -0
- {refrain-0.2.5 → refrain-0.2.7}/pyproject.toml +1 -1
- refrain-0.2.7/src/refrain/__init__.py +1 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/app.py +98 -16
- refrain-0.2.7/src/refrain/assets/icons/menu-quit.svg +3 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/autostart.py +24 -6
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/config.py +112 -8
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/cover_art.py +39 -11
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/cover_fetcher.py +28 -10
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/discord_rpc.py +24 -1
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/i18n/refrain_de.qm +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/i18n/refrain_de.ts +14 -14
- refrain-0.2.7/src/refrain/i18n/refrain_es.qm +0 -0
- refrain-0.2.7/src/refrain/i18n/refrain_es.ts +673 -0
- refrain-0.2.7/src/refrain/i18n/refrain_fr.qm +0 -0
- refrain-0.2.7/src/refrain/i18n/refrain_fr.ts +673 -0
- refrain-0.2.7/src/refrain/i18n/refrain_it.qm +0 -0
- refrain-0.2.7/src/refrain/i18n/refrain_it.ts +673 -0
- refrain-0.2.7/src/refrain/i18n/refrain_ja.qm +0 -0
- refrain-0.2.7/src/refrain/i18n/refrain_ja.ts +673 -0
- refrain-0.2.7/src/refrain/i18n/refrain_pl.qm +0 -0
- refrain-0.2.7/src/refrain/i18n/refrain_pl.ts +673 -0
- refrain-0.2.7/src/refrain/i18n/refrain_pt.qm +0 -0
- refrain-0.2.7/src/refrain/i18n/refrain_pt.ts +673 -0
- refrain-0.2.7/src/refrain/i18n/refrain_ru.qm +0 -0
- refrain-0.2.7/src/refrain/i18n/refrain_ru.ts +673 -0
- refrain-0.2.7/src/refrain/i18n/refrain_zh_CN.qm +0 -0
- refrain-0.2.7/src/refrain/i18n/refrain_zh_CN.ts +673 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/logging_setup.py +6 -1
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/single_instance.py +18 -3
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/ui/settings_window.py +39 -7
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/ui/tray.py +70 -15
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/ui/update_dialog.py +5 -1
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/updater.py +47 -2
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_config.py +74 -0
- refrain-0.2.5/docs/screenshots/live-log.png +0 -0
- refrain-0.2.5/docs/screenshots/settings-updates.png +0 -0
- refrain-0.2.5/docs/screenshots/tray-menu.png +0 -0
- refrain-0.2.5/docs/screenshots/update-dialog.png +0 -0
- refrain-0.2.5/src/refrain/__init__.py +0 -1
- {refrain-0.2.5 → refrain-0.2.7}/.editorconfig +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/.gitattributes +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/.github/FUNDING.yml +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/.github/codeql/codeql-config.yml +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/.github/dependabot.yml +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/.github/pull_request_template.md +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/.github/workflows/codeql.yml +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/.github/workflows/security.yml +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/.github/workflows/tests.yml +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/.gitignore +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/CODE_OF_CONDUCT.md +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/CONTRIBUTING.md +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/LICENSE +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/Makefile +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/SECURITY.md +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/docs/architecture.md +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/docs/bluetooth.md +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/docs/faq.md +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/docs/screenshots/discord-rpc.png +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/docs/screenshots/notification.png +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/docs/screenshots/welcome.png +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/docs/test-matrix.md +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/packaging/README.md +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/packaging/aur/refrain-git/PKGBUILD +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/packaging/flatpak/io.github.Rockykln.Refrain.desktop +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/packaging/flatpak/io.github.Rockykln.Refrain.yaml +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/packaging/flatpak/python-deps.json +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/requirements-dev.txt +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/requirements.txt +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/__main__.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/assets/icons/github-mark.svg +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/assets/icons/menu-update.svg +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/assets/icons/refrain.png +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/assets/icons/refrain.svg +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/assets/icons/tray-paused-dark.svg +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/assets/icons/tray-paused.svg +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/assets/icons/tray-playing-dark.svg +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/assets/icons/tray-playing.svg +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/assets/icons/tray-stopped-dark.svg +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/assets/icons/tray-stopped.svg +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/assets/refrain.desktop +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/daemon.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/paths.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/sources/__init__.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/sources/base.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/sources/bluetooth.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/sources/mpris.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/sources/mpris_server.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/timing.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/ui/__init__.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/ui/log_window.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/src/refrain/ui/welcome_dialog.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/__init__.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/conftest.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_autostart.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_cover_art.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_cover_fetcher.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_daemon_album_format.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_daemon_idle.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_discord_listening.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_discord_rpc.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_imports.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_mpris_dispatch.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_paths.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_sources_base.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_timing.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_tray_init.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_update_dialog_body.py +0 -0
- {refrain-0.2.5 → refrain-0.2.7}/tests/test_updater.py +0 -0
|
@@ -104,10 +104,38 @@ jobs:
|
|
|
104
104
|
mv *.AppImage.zsync ../../dist/ 2>/dev/null || true
|
|
105
105
|
ls -la ../../dist/
|
|
106
106
|
|
|
107
|
+
# Extract the matching `## [X.Y.Z]` section from CHANGELOG.md
|
|
108
|
+
# and feed it into the GitHub release body. Without this we'd
|
|
109
|
+
# fall back to GitHub's auto-generated "Full Changelog: …"
|
|
110
|
+
# one-liner — which is also what the in-app
|
|
111
|
+
# Settings → Updates → "Latest release notes" pane renders, so
|
|
112
|
+
# users would only ever see the bare compare-link there.
|
|
113
|
+
- name: Extract CHANGELOG section for the release body
|
|
114
|
+
run: |
|
|
115
|
+
TAG="${GITHUB_REF#refs/tags/v}"
|
|
116
|
+
# awk: print everything between `## [TAG]` and the next
|
|
117
|
+
# top-level ## section, excluding the heading lines themselves.
|
|
118
|
+
awk -v ver="${TAG}" '
|
|
119
|
+
$0 ~ "^## \\[" ver "\\]" { p=1; next }
|
|
120
|
+
p && /^## \[/ { exit }
|
|
121
|
+
p { print }
|
|
122
|
+
' CHANGELOG.md > release-notes.md
|
|
123
|
+
echo "::group::release-notes.md"
|
|
124
|
+
cat release-notes.md
|
|
125
|
+
echo "::endgroup::"
|
|
126
|
+
if [ ! -s release-notes.md ]; then
|
|
127
|
+
echo "::error::CHANGELOG section for v${TAG} extracted to nothing"
|
|
128
|
+
exit 1
|
|
129
|
+
fi
|
|
130
|
+
|
|
107
131
|
- name: Create GitHub release
|
|
108
132
|
uses: softprops/action-gh-release@v3
|
|
109
133
|
with:
|
|
110
|
-
|
|
134
|
+
# Use our CHANGELOG section (extracted above), not GitHub's
|
|
135
|
+
# auto-generated notes — the in-app update dialog renders
|
|
136
|
+
# this body verbatim so it has to actually be the changelog.
|
|
137
|
+
body_path: release-notes.md
|
|
138
|
+
generate_release_notes: false
|
|
111
139
|
draft: false
|
|
112
140
|
prerelease: ${{ contains(github.ref, '-') }} # e.g. v0.2.0-rc1
|
|
113
141
|
name: "Refrain ${{ github.ref_name }}"
|
|
@@ -7,6 +7,226 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.7] - 2026-05-07
|
|
11
|
+
|
|
12
|
+
Tray-menu polish + Discord-RPC reliability fixes. Every tray action
|
|
13
|
+
now carries an icon, song-info rows render in normal menu text
|
|
14
|
+
colour instead of greyed-out indented rows, middle-click on the
|
|
15
|
+
tray toggles play/pause, and Discord handshake errors no longer
|
|
16
|
+
spam the log with unexpected-exception tracebacks.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **Middle-click on the tray icon toggles play/pause.** Clicks the
|
|
21
|
+
PlayPause command on whichever MPRIS source Refrain is currently
|
|
22
|
+
attached to — same path as the tray-menu Play/Pause item, but
|
|
23
|
+
reachable without opening the menu. Plain left-click still opens
|
|
24
|
+
Settings; right-click still shows the menu.
|
|
25
|
+
- **Tray menu actions now all carry icons.** Once any QMenu item
|
|
26
|
+
has an icon, the menu reserves the icon column for *all* items —
|
|
27
|
+
so adding the red ✕ to Quit Refrain made every previously
|
|
28
|
+
text-only action read as misaligned with blank icon space. Action
|
|
29
|
+
items now use freedesktop theme icons (`configure`,
|
|
30
|
+
`view-list-text`, `view-refresh`, `media-skip-backward`,
|
|
31
|
+
`media-playback-start`, `media-playback-pause`,
|
|
32
|
+
`media-skip-forward`) which match the user's Plasma / GNOME /
|
|
33
|
+
Breeze icon set. Update + Quit keep their bundled coloured-accent
|
|
34
|
+
SVGs. Source strings for the playback / restart actions dropped
|
|
35
|
+
their unicode-glyph prefixes (`⏮ ⏵ ⏸ ⏭ ⟳`) so the new icons
|
|
36
|
+
don't read as duplicates; all 9 .ts files updated to match. The
|
|
37
|
+
Quit ✕ glyph was redrawn to fill its 16×16 viewBox edge-to-edge
|
|
38
|
+
so it visually matches the theme icons' pixel weight.
|
|
39
|
+
- **Song-info rows in the tray menu now render with proper icons
|
|
40
|
+
and full-strength text colour.** Title / Artist / Progress /
|
|
41
|
+
Discord-status used to be `setEnabled(False)` so KDE's DBusMenu
|
|
42
|
+
rendered them muted-grey + indented + iconless next to the white
|
|
43
|
+
action labels below. They're now enabled (a stray click opens
|
|
44
|
+
Settings — their natural "tell me more" target) and carry
|
|
45
|
+
`view-media-track`, `view-media-artist`, `chronometer`, and
|
|
46
|
+
`network-connect` / `network-disconnect` icons. The `♪` prefix
|
|
47
|
+
on the title and the `●/○` prefix on the Discord-status row are
|
|
48
|
+
gone — the icons replace them.
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- **Settings → Updates "Latest release notes" QTextBrowser was
|
|
53
|
+
parent-less.** `_build_updates_tab` called
|
|
54
|
+
`notes_group.setLayout(QVBoxLayout())` on a QGroupBox that
|
|
55
|
+
already had a QFormLayout installed by `_new_group`. Qt refuses
|
|
56
|
+
to install a second layout (logs "QLayout: Attempting to add
|
|
57
|
+
QLayout to QGroupBox which already has a layout") so the new
|
|
58
|
+
VBoxLayout was discarded and the QTextBrowser ended up as a
|
|
59
|
+
free-floating top-level widget kept alive only by the Python
|
|
60
|
+
reference. In production it still painted because the parent
|
|
61
|
+
page laid it out somewhere reasonable, but it was outside the
|
|
62
|
+
widget tree (`findChildren(QTextBrowser)` returned 0). The view
|
|
63
|
+
is now added via the existing form layout's `addRow`.
|
|
64
|
+
- **Discord handshake errors logged as ERROR-level unexpected
|
|
65
|
+
exceptions with a full traceback every retry.** When Discord
|
|
66
|
+
responded to a `pypresence.Presence.connect()` with an error
|
|
67
|
+
message (e.g. "User logged out" — DiscordError code 1000, or
|
|
68
|
+
"Invalid Client ID"), the bare `except Exception:` clause in
|
|
69
|
+
`_ensure_connected` swallowed it as a programmer bug and emitted
|
|
70
|
+
a noisy traceback every backoff cycle. `pypresence.exceptions.
|
|
71
|
+
DiscordError` is now caught explicitly and logged at INFO; the
|
|
72
|
+
back-off jumps straight to the 15 s ceiling so Refrain doesn't
|
|
73
|
+
retry tightly while the user is signing back into Discord or
|
|
74
|
+
fixing a typo'd client_id.
|
|
75
|
+
- **First Discord push after a forced reconnect could be deduped
|
|
76
|
+
out of existence.** The presence-update dedupe cache lived on
|
|
77
|
+
the orchestrator across `Presence` instance replacements; after
|
|
78
|
+
a reconnect with byte-identical payload data, the new pipe
|
|
79
|
+
carried no state on Discord's side but our cache still claimed
|
|
80
|
+
"we already pushed this", so the user saw "no activity" until
|
|
81
|
+
metadata changed. `_last_payload` is now reset to None in the
|
|
82
|
+
successful-connect path of `_ensure_connected`.
|
|
83
|
+
- **Empty artist row in tray menu when nothing was playing.** The
|
|
84
|
+
artist QAction stayed visible with empty text whenever no track
|
|
85
|
+
was active, rendering as a tall blank line right under
|
|
86
|
+
"(nothing playing)". Now hidden until a real track populates it.
|
|
87
|
+
- **Settings → Updates "Latest release notes" pane stayed empty
|
|
88
|
+
on every Refrain restart within the 24-hour update-check
|
|
89
|
+
cooldown.** `maybe_check_on_startup` short-circuited *before*
|
|
90
|
+
fetching anything when the cooldown was active, so
|
|
91
|
+
`releaseInfoFetched` never fired and `set_latest_release` never
|
|
92
|
+
populated the inline pane. The v0.2.6 workaround — re-fetching
|
|
93
|
+
on Updates-tab activation with `manual=True` — additionally
|
|
94
|
+
fired the "up to date" / "update available" popups on every tab
|
|
95
|
+
visit, which was its own bug. Startup now always fetches the
|
|
96
|
+
release info (silent, no popups) so the pane is populated
|
|
97
|
+
immediately; the cooldown only gates whether that fetch *also*
|
|
98
|
+
triggers the auto-popup. The on-tab-activation fetch is gone.
|
|
99
|
+
|
|
100
|
+
## [0.2.6] - 2026-05-07
|
|
101
|
+
|
|
102
|
+
Localization expansion plus a bundle of reliability fixes accumulated
|
|
103
|
+
since v0.2.5 across the config layer, restart path, cover cache, IPC
|
|
104
|
+
sandbox bridge, and update flow.
|
|
105
|
+
|
|
106
|
+
### Added
|
|
107
|
+
|
|
108
|
+
- **8 new UI languages.** Refrain ships full translations (127 / 127
|
|
109
|
+
strings each) for Spanish (`es`), French (`fr`), Portuguese (`pt`),
|
|
110
|
+
Italian (`it`), Russian (`ru`), Polish (`pl`), Japanese (`ja`), and
|
|
111
|
+
Simplified Chinese (`zh_CN`) on top of the existing English source
|
|
112
|
+
+ German. The Settings → Advanced → Language dropdown lists each
|
|
113
|
+
language under its native endonym ("Español", "Français",
|
|
114
|
+
"Português", "Italiano", "Русский", "Polski", "日本語",
|
|
115
|
+
"简体中文"). Picking a language restarts Refrain so the
|
|
116
|
+
QTranslator picks up the new `.qm`. Together with English and
|
|
117
|
+
German, this covers roughly 3 billion native speakers.
|
|
118
|
+
|
|
119
|
+
### Fixed
|
|
120
|
+
|
|
121
|
+
- **GitHub release bodies were one-line "Full Changelog: …"** — the
|
|
122
|
+
release workflow used `generate_release_notes: true`, which is
|
|
123
|
+
GitHub's auto-generated content (just a compare-link to the
|
|
124
|
+
previous tag). The in-app inline release-notes pane and the
|
|
125
|
+
popup dialog rendered that 79-character body verbatim. The
|
|
126
|
+
workflow now extracts the matching `## [X.Y.Z]` section from
|
|
127
|
+
CHANGELOG.md via awk and feeds it as the release `body_path`.
|
|
128
|
+
Existing v0.1.0 – v0.2.5 release bodies were backfilled out-of-
|
|
129
|
+
band via `gh release edit --notes-file …` against the same
|
|
130
|
+
extraction.
|
|
131
|
+
- **Release-notes pane stayed empty after every Refrain restart** —
|
|
132
|
+
the orchestrator's 24-hour startup cooldown blocks
|
|
133
|
+
`maybe_check_on_startup` whenever `last_check_ts` is recent, and
|
|
134
|
+
a fresh process has no cached `_latest`. `SettingsWindow` now
|
|
135
|
+
emits `updatesTabRequested` when the user activates the Updates
|
|
136
|
+
tab; app.py wires that to a slot that triggers
|
|
137
|
+
`updater.check_now(manual=True)` only when no cached release is
|
|
138
|
+
available this session. First visit fetches in the background;
|
|
139
|
+
subsequent visits use the cache.
|
|
140
|
+
- **`os.execvp` failure during Restart had no fallback** — if
|
|
141
|
+
`sys.argv[0]` wasn't a runnable script (typical of
|
|
142
|
+
`python -m refrain` source-checkout invocation, where argv[0] is
|
|
143
|
+
`__main__.py`), execvp raised an unhandled OSError. Detect
|
|
144
|
+
unrunnable paths up front and fall back to
|
|
145
|
+
`<sys.executable> -m refrain`, which always works because
|
|
146
|
+
Refrain is currently importable. The fallback is also retried
|
|
147
|
+
from the actual OSError handler if the first execvp fails for
|
|
148
|
+
any other reason.
|
|
149
|
+
- **`Config.save` leaked .tmp files on write failure** — a disk-
|
|
150
|
+
full / read-only / permission-denied error during the atomic
|
|
151
|
+
write left a stale `*.tmp` sibling next to the real config
|
|
152
|
+
forever. Now wrapped in a `try/finally` that unlinks the partial
|
|
153
|
+
file before re-raising.
|
|
154
|
+
- **`Config._format_value` didn't escape newline / tab / CR** —
|
|
155
|
+
any string value with those characters (extremely rare in our
|
|
156
|
+
schema, but possible via a hand-edited config or a future
|
|
157
|
+
field) would produce broken TOML that tomllib rejects on next
|
|
158
|
+
load, tripping the "config unreadable, using defaults" fallback
|
|
159
|
+
and silently losing every setting. Added `\n` / `\r` / `\t`
|
|
160
|
+
escapes alongside the existing `\\` / `\"`.
|
|
161
|
+
- **CHANGELOG link `[0.2.1]` was a 404** — pointed at a non-
|
|
162
|
+
existent `v0.2.1` tag (the v0.2.1 commit was made but never
|
|
163
|
+
tagged or published; its content rolled into v0.2.2). Replaced
|
|
164
|
+
with a commit-hash link to `6b339ba`. ROADMAP gained a matching
|
|
165
|
+
`(rolled into v0.2.2; never separately tagged)` suffix.
|
|
166
|
+
- **Settings → Apply with a non-writable config directory crashed
|
|
167
|
+
silently.** `Config.save` raised OSError (read-only home, disk
|
|
168
|
+
full, permissions). PySide6's signal-slot machinery logged
|
|
169
|
+
"uncaught exception in slot" and Apply visibly did nothing.
|
|
170
|
+
Now wrapped in try/except: the user gets a `QMessageBox.critical`
|
|
171
|
+
naming the path + the OS error, and the in-memory daemon state
|
|
172
|
+
still updates so the rest of the session works.
|
|
173
|
+
- **`autostart.enable` / `disable` raised on filesystem failure**
|
|
174
|
+
with no user feedback. Now they return a bool and log a warning
|
|
175
|
+
on failure; Settings → Apply continues instead of crashing.
|
|
176
|
+
- **Update dialog `_on_update_clicked` could TypeError on a
|
|
177
|
+
re-entered click** — `close_btn.clicked.disconnect()` raises if
|
|
178
|
+
the signal has no connections (unusual but possible during
|
|
179
|
+
fast double-clicks while runner setup is in flight). Wrapped
|
|
180
|
+
in `contextlib.suppress(TypeError)` like the matching
|
|
181
|
+
`_on_runner_finished` path.
|
|
182
|
+
- **Refrain crashed at startup when no D-Bus session bus was
|
|
183
|
+
available** — `single_instance.acquire()` propagated
|
|
184
|
+
`dbus.DBusException` past `app.py`'s `except AlreadyRunning`,
|
|
185
|
+
giving the user an unhandled exception traceback instead of a
|
|
186
|
+
message box. New `SessionBusUnavailable` exception type
|
|
187
|
+
surfaces the actual reason ("D-Bus session bus unavailable")
|
|
188
|
+
with hints about `dbus-daemon` / `DBUS_SESSION_BUS_ADDRESS`.
|
|
189
|
+
- **`setup_logging(level)` and `_apply_log_level` crashed on
|
|
190
|
+
non-string `log_level`** — a hand-edited config with
|
|
191
|
+
`log_level = false` (boolean) or `log_level = 5` (int) would
|
|
192
|
+
AttributeError on `.upper()`. Now coerced to str defensively.
|
|
193
|
+
- **`Config._construct` accepted wrongly-typed values silently** —
|
|
194
|
+
`cover_cache_size = "200"` (string) would survive the dataclass
|
|
195
|
+
__init__ but then crash in `_prune_cover_cache` with TypeError on
|
|
196
|
+
`len(files) - "200"`. The new `_coerce_value()` helper coerces
|
|
197
|
+
primitives where it makes sense (int from numeric string,
|
|
198
|
+
bool from "true"/"false") and drops + warns when it doesn't
|
|
199
|
+
(e.g. `log_level = false` → use the field default). 1 new test
|
|
200
|
+
in test_config.py.
|
|
201
|
+
- **Cover-art cache writes leaked .tmp files on disk failure** —
|
|
202
|
+
`cover_art._write_cache` and `download_cover_image` called
|
|
203
|
+
`tmp.write_bytes()` / `os.replace()` without exception
|
|
204
|
+
handling. A read-only `~/.cache/refrain/covers/` left a stale
|
|
205
|
+
`*.tmp` next to the real file forever. Both now wrap in
|
|
206
|
+
try/except, clean up the partial tmp, and log debug.
|
|
207
|
+
- **`_bridge_sandboxed_ipc_socket` could raise on locked-down
|
|
208
|
+
sandbox dirs** — a permission-denied `iterdir()` of one of the
|
|
209
|
+
Snap/Flatpak candidate paths used to propagate up through
|
|
210
|
+
`DiscordRPC._ensure_connected` → daemon `_tick`'s except, killing
|
|
211
|
+
the connect attempt every poll. Now wrapped: failures log debug
|
|
212
|
+
and the connect proceeds without sandbox-bridging.
|
|
213
|
+
- **iTunes search shape-check.** A malformed response (string
|
|
214
|
+
error, list-not-dict, missing fields) used to AttributeError
|
|
215
|
+
on `data.get("results", [])` or `first.get(...)`. Now both
|
|
216
|
+
layers check `isinstance(...)` first and treat unexpected
|
|
217
|
+
shapes as "no result" — caller falls back to the bundled
|
|
218
|
+
brand icon.
|
|
219
|
+
- **`_prune_cover_cache` could crash daemon startup** on a
|
|
220
|
+
permission-denied or corrupt cache dir. Now wraps the
|
|
221
|
+
enumerate/sort in try/except, logs debug, returns 0 — the
|
|
222
|
+
daemon starts normally and lives without prune until next
|
|
223
|
+
session. Plus defensive int() coercion of `max_entries`.
|
|
224
|
+
|
|
225
|
+
### Tests
|
|
226
|
+
|
|
227
|
+
- 3 new in `tests/test_config.py` covering newline-escape
|
|
228
|
+
round-trip and the .tmp cleanup-on-failure path.
|
|
229
|
+
|
|
10
230
|
## [0.2.5] - 2026-05-07
|
|
11
231
|
|
|
12
232
|
A same-day patch on top of v0.2.4 driven by user feedback that the
|
|
@@ -411,6 +631,11 @@ of reliability + UX work driven by live-testing the v0.2.1 build.
|
|
|
411
631
|
|
|
412
632
|
## [0.2.1] - 2026-05-06
|
|
413
633
|
|
|
634
|
+
> **Note:** v0.2.1 was never tagged or published as a separate
|
|
635
|
+
> release. The changes documented below were rolled into the
|
|
636
|
+
> subsequent v0.2.2 release. Kept here for changelog continuity —
|
|
637
|
+
> see [v0.2.2](#022---2026-05-06) for the actually-shipped delta.
|
|
638
|
+
|
|
414
639
|
Polish release built around the v0.2.0 surface. The Settings window got
|
|
415
640
|
a UI overhaul, two notification cover-art races are gone, and the
|
|
416
641
|
external code-review punch list landed in full (atomic config writes,
|
|
@@ -844,12 +1069,14 @@ with a proper, installable Linux app.
|
|
|
844
1069
|
pip-audit, trufflehog, release), Dependabot, issue + PR templates,
|
|
845
1070
|
`CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`.
|
|
846
1071
|
|
|
847
|
-
[Unreleased]: https://github.com/Rockykln/refrain/compare/v0.2.
|
|
1072
|
+
[Unreleased]: https://github.com/Rockykln/refrain/compare/v0.2.7...HEAD
|
|
1073
|
+
[0.2.7]: https://github.com/Rockykln/refrain/compare/v0.2.6...v0.2.7
|
|
1074
|
+
[0.2.6]: https://github.com/Rockykln/refrain/compare/v0.2.5...v0.2.6
|
|
848
1075
|
[0.2.5]: https://github.com/Rockykln/refrain/compare/v0.2.4...v0.2.5
|
|
849
1076
|
[0.2.4]: https://github.com/Rockykln/refrain/compare/v0.2.3...v0.2.4
|
|
850
1077
|
[0.2.3]: https://github.com/Rockykln/refrain/compare/v0.2.2...v0.2.3
|
|
851
1078
|
[0.2.2]: https://github.com/Rockykln/refrain/compare/v0.2.1...v0.2.2
|
|
852
|
-
[0.2.1]: https://github.com/Rockykln/refrain/
|
|
1079
|
+
[0.2.1]: https://github.com/Rockykln/refrain/commit/6b339ba
|
|
853
1080
|
[0.2.0]: https://github.com/Rockykln/refrain/compare/v0.1.5...v0.2.0
|
|
854
1081
|
[0.1.5]: https://github.com/Rockykln/refrain/compare/v0.1.4...v0.1.5
|
|
855
1082
|
[0.1.4]: https://github.com/Rockykln/refrain/compare/v0.1.3...v0.1.4
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: refrain
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
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
|
|
@@ -153,6 +153,15 @@ registers their own (free, takes 30 seconds):
|
|
|
153
153
|
4. Launch Refrain → *Settings → General → Discord Client ID* → paste,
|
|
154
154
|
*Apply*.
|
|
155
155
|
|
|
156
|
+
The first time you launch Refrain without a configured ID, the
|
|
157
|
+
welcome wizard pops up with the setup steps + a live diagnostics
|
|
158
|
+
panel that probes your D-Bus session and Discord IPC socket so you
|
|
159
|
+
know up front whether your environment can host the RPC at all.
|
|
160
|
+
|
|
161
|
+
<p align="center">
|
|
162
|
+
<img src="docs/screenshots/welcome.png" alt="Welcome wizard" width="560"/>
|
|
163
|
+
</p>
|
|
164
|
+
|
|
156
165
|
That's it. The status will appear in Discord on the next track change.
|
|
157
166
|
|
|
158
167
|
## Configuration
|
|
@@ -187,7 +196,7 @@ poll_interval_ms = 500
|
|
|
187
196
|
log_level = "INFO"
|
|
188
197
|
cover_cache_size = 200 # disk cap for cached covers
|
|
189
198
|
idle_grace_s = 30 # clear status when same track plays past duration + grace; 0 disables
|
|
190
|
-
language = "system" # "system" follows QLocale; "en"
|
|
199
|
+
language = "system" # "system" follows QLocale; "en", "de", "es", "fr", "pt", "it", "ru", "pl", "ja", "zh_CN" force a translation
|
|
191
200
|
```
|
|
192
201
|
|
|
193
202
|
Per-source `client_id_*` fields let Apple Music render under one Discord
|
|
@@ -119,6 +119,15 @@ registers their own (free, takes 30 seconds):
|
|
|
119
119
|
4. Launch Refrain → *Settings → General → Discord Client ID* → paste,
|
|
120
120
|
*Apply*.
|
|
121
121
|
|
|
122
|
+
The first time you launch Refrain without a configured ID, the
|
|
123
|
+
welcome wizard pops up with the setup steps + a live diagnostics
|
|
124
|
+
panel that probes your D-Bus session and Discord IPC socket so you
|
|
125
|
+
know up front whether your environment can host the RPC at all.
|
|
126
|
+
|
|
127
|
+
<p align="center">
|
|
128
|
+
<img src="docs/screenshots/welcome.png" alt="Welcome wizard" width="560"/>
|
|
129
|
+
</p>
|
|
130
|
+
|
|
122
131
|
That's it. The status will appear in Discord on the next track change.
|
|
123
132
|
|
|
124
133
|
## Configuration
|
|
@@ -153,7 +162,7 @@ poll_interval_ms = 500
|
|
|
153
162
|
log_level = "INFO"
|
|
154
163
|
cover_cache_size = 200 # disk cap for cached covers
|
|
155
164
|
idle_grace_s = 30 # clear status when same track plays past duration + grace; 0 disables
|
|
156
|
-
language = "system" # "system" follows QLocale; "en"
|
|
165
|
+
language = "system" # "system" follows QLocale; "en", "de", "es", "fr", "pt", "it", "ru", "pl", "ja", "zh_CN" force a translation
|
|
157
166
|
```
|
|
158
167
|
|
|
159
168
|
Per-source `client_id_*` fields let Apple Music render under one Discord
|
|
@@ -145,7 +145,7 @@ What's done, what's next, what's deliberately not in scope.
|
|
|
145
145
|
- **Window-title cleanup** — no more "Refrain — Settings — Refrain"
|
|
146
146
|
triple; Qt's auto-suffix is the only place "Refrain" appears.
|
|
147
147
|
|
|
148
|
-
## Done — v0.2.1
|
|
148
|
+
## Done — v0.2.1 (rolled into v0.2.2; never separately tagged)
|
|
149
149
|
|
|
150
150
|
- **Settings UI overhaul** — consistent `QGroupBox` + form-layout
|
|
151
151
|
across every tab, left-aligned group titles via stylesheet
|
|
@@ -285,14 +285,97 @@ What's done, what's next, what's deliberately not in scope.
|
|
|
285
285
|
|
|
286
286
|
## Up next — v0.3
|
|
287
287
|
|
|
288
|
-
- **Polishing the wrapped i18n surface** — wrap the remaining
|
|
289
|
-
fixed-position strings (date formatters, advanced-tab subtitles,
|
|
290
|
-
bluetooth picker labels) so the German build covers everything a
|
|
291
|
-
user sees, not just the high-traffic widgets.
|
|
292
288
|
- **Stable-release AUR build** that doesn't rely on the GitHub
|
|
293
289
|
release tarball — switch to a `git`-source PKGBUILD pinned to the
|
|
294
290
|
signed tag, so AUR users get the exact same commit the release
|
|
295
291
|
workflow ships.
|
|
292
|
+
- **Wrap the remaining fixed-position strings** (date formatters,
|
|
293
|
+
bluetooth picker labels) so every language reaches 100% coverage,
|
|
294
|
+
not just the high-traffic widgets.
|
|
295
|
+
|
|
296
|
+
## Done — v0.2.7
|
|
297
|
+
|
|
298
|
+
- **Tray-menu polish.** Every action carries an icon now —
|
|
299
|
+
freedesktop theme icons (`configure`, `view-list-text`,
|
|
300
|
+
`view-refresh`, `media-skip-{backward,forward}`,
|
|
301
|
+
`media-playback-{start,pause}`) for built-in actions plus the
|
|
302
|
+
bundled coloured-accent SVGs for Update / Quit. Source strings
|
|
303
|
+
for the playback / restart / Discord-status entries dropped
|
|
304
|
+
their unicode-glyph prefixes (`⏮ ⏵ ⏸ ⏭ ⟳ ●/○`); all 9 .ts
|
|
305
|
+
files updated. Quit ✕ glyph redrawn to fill its 16×16 viewBox.
|
|
306
|
+
- **Song-info rows render with proper colour + icons.** Title /
|
|
307
|
+
Artist / Progress / Discord-status used to be `setEnabled(False)`
|
|
308
|
+
so KDE / GNOME's DBusMenu painted them muted-grey + indented +
|
|
309
|
+
iconless. They're now enabled (a click opens Settings) and
|
|
310
|
+
carry `view-media-{track,artist}`, `chronometer`, and
|
|
311
|
+
`network-{connect,disconnect}` icons.
|
|
312
|
+
- **Middle-click on the tray icon toggles play/pause.** Same
|
|
313
|
+
PlayPause command as the tray-menu Play/Pause item, but
|
|
314
|
+
reachable without opening the menu.
|
|
315
|
+
- **Discord handshake errors no longer crash-log.** `pypresence.
|
|
316
|
+
exceptions.DiscordError` (raised when Discord rejects the
|
|
317
|
+
handshake — "User logged out", "Invalid Client ID") fell
|
|
318
|
+
through the bare `except Exception:` and emitted a full
|
|
319
|
+
traceback every retry cycle. Now caught explicitly, logged at
|
|
320
|
+
INFO, with backoff jumping straight to the 15 s ceiling so
|
|
321
|
+
Refrain doesn't retry tightly while you sign back into Discord.
|
|
322
|
+
- **First Discord push after a forced reconnect can no longer be
|
|
323
|
+
deduped out.** `_last_payload` is reset in the connect-success
|
|
324
|
+
path so the dedup cache from a previous Presence instance
|
|
325
|
+
doesn't suppress the first push on the new pipe.
|
|
326
|
+
- **Settings → Updates "Latest release notes" pane populates on
|
|
327
|
+
every restart.** v0.2.6's startup auto-check short-circuited
|
|
328
|
+
before fetching when the 24h cooldown was active; the v0.2.6
|
|
329
|
+
workaround re-fetched on every Updates-tab visit. Both gone —
|
|
330
|
+
startup runs a silent fetch (no popups, no cooldown bump) so
|
|
331
|
+
the pane is populated immediately, while the cooldown still
|
|
332
|
+
gates the auto-nag popup.
|
|
333
|
+
- **Empty artist row in tray menu when nothing was playing**
|
|
334
|
+
(`setVisible(False)` until a real track populates it).
|
|
335
|
+
|
|
336
|
+
## Done — v0.2.6
|
|
337
|
+
|
|
338
|
+
- **8 new UI languages** at 127/127 strings each: Spanish (`es`),
|
|
339
|
+
French (`fr`), Portuguese (`pt`), Italian (`it`), Russian
|
|
340
|
+
(`ru`), Polish (`pl`), Japanese (`ja`), Simplified Chinese
|
|
341
|
+
(`zh_CN`). Settings → Advanced → Language dropdown lists each
|
|
342
|
+
under its native endonym. Together with English + German this
|
|
343
|
+
covers ~3 billion native speakers.
|
|
344
|
+
- **GitHub release bodies were one-line "Full Changelog: …"**
|
|
345
|
+
because the workflow used `generate_release_notes: true`. Now
|
|
346
|
+
extracts the matching `## [X.Y.Z]` section from CHANGELOG.md
|
|
347
|
+
via awk and feeds it as `body_path`; existing v0.1.0 – v0.2.5
|
|
348
|
+
bodies were backfilled out-of-band.
|
|
349
|
+
- **Reliability sweep**: `os.execvp` Restart fallback to
|
|
350
|
+
`python -m refrain` when `argv[0]` isn't runnable;
|
|
351
|
+
Config.save .tmp cleanup on disk-full / read-only;
|
|
352
|
+
`Config._format_value` escapes `\n / \r / \t`;
|
|
353
|
+
`Config._construct` coerces wrongly-typed primitives (e.g.
|
|
354
|
+
`cover_cache_size = "200"`) instead of crashing in
|
|
355
|
+
`_prune_cover_cache`; cover-art `_write_cache` +
|
|
356
|
+
`download_cover_image` clean up `.tmp` siblings on disk
|
|
357
|
+
failure; `_bridge_sandboxed_ipc_socket` permission-error wrap;
|
|
358
|
+
iTunes Search shape-checks; `SessionBusUnavailable` exception
|
|
359
|
+
surfacing instead of bare DBusException; Apply error toast for
|
|
360
|
+
non-writable config dirs; `setup_logging`/`_apply_log_level`
|
|
361
|
+
coerce non-string `log_level`.
|
|
362
|
+
|
|
363
|
+
## Done — v0.2.5
|
|
364
|
+
|
|
365
|
+
- **AUR + Flatpak self-update spawns a terminal automatically**
|
|
366
|
+
running the package-manager command (`yay -Syu refrain` /
|
|
367
|
+
`flatpak update io.github.Rockykln.Refrain`) instead of just
|
|
368
|
+
showing the command in a popup. Falls back to the previous
|
|
369
|
+
message-box hint when no terminal is on PATH (probe list:
|
|
370
|
+
konsole, gnome-terminal, xfce4-terminal, kitty, alacritty,
|
|
371
|
+
foot, xterm, wezterm).
|
|
372
|
+
- **pip auto-update detects "no-op" exits.** PyPI's CDN can lag
|
|
373
|
+
the GitHub Releases API by minutes, so a "0.2.X available"
|
|
374
|
+
check could succeed while pip exited 0 with "Requirement
|
|
375
|
+
already satisfied" (no upgrade). Refrain now parses pip's
|
|
376
|
+
stdout to distinguish "Successfully installed" from
|
|
377
|
+
"Requirement already satisfied" and tells the user to retry
|
|
378
|
+
shortly instead of restarting into the same version.
|
|
296
379
|
|
|
297
380
|
## Done — v0.2.2
|
|
298
381
|
|
|
@@ -4,14 +4,16 @@ The README references screenshots from this directory. To regenerate them:
|
|
|
4
4
|
|
|
5
5
|
| File | What to capture |
|
|
6
6
|
|----------------------------|------------------------------------------------------|
|
|
7
|
-
| `tray-menu.png` | Right-click the Refrain tray icon while a track plays.
|
|
7
|
+
| `tray-menu.png` | Right-click the Refrain tray icon while a track plays. Captures title / artist / progress / Discord-status info rows + Previous / Play-Pause / Next / Settings / Live log / Restart / Quit, all with theme icons. |
|
|
8
8
|
| `settings-general.png` | Settings window → *General* tab, showing Discord client ID, Privacy combo, autostart, notifications, cover art, buttons. |
|
|
9
9
|
| `settings-sources.png` | Settings window → *Sources* tab, showing MPRIS toggle, Bluetooth toggle, and the paired-device dropdown populated. |
|
|
10
|
-
| `settings-updates.png` | Settings window → *Updates* tab. Auto-check on, last-checked timestamp visible,
|
|
10
|
+
| `settings-updates.png` | Settings window → *Updates* tab. Auto-check on, last-checked timestamp visible, the "Latest release notes" pane populated from CHANGELOG. |
|
|
11
|
+
| `settings-advanced.png` | Settings window → *Advanced* tab, showing the Language dropdown (11 entries: System default + 10 native endonyms), poll interval, log level, cover cache size, idle grace, and the Reset-all-settings button. |
|
|
11
12
|
| `update-dialog.png` | The update-available dialog (only appears when a new version is found). Trigger it locally by editing `__version__` to something older and clicking *Check for updates now*. |
|
|
12
13
|
| `discord-rpc.png` | Cropped section of your Discord profile showing the "Listening to" card with cover art + elapsed/total timer. |
|
|
13
14
|
| `notification.png` | A desktop notification fired by Refrain on track change. KDE Plasma's notification with the album cover at left. |
|
|
14
15
|
| `live-log.png` | The live-log window, opened via tray → *Live log…*, with at least 10 lines of streamed log. |
|
|
16
|
+
| `welcome.png` | First-run welcome wizard — shown when no `client_id` is configured yet. Diagnostics panel + Discord-app guidance. |
|
|
15
17
|
|
|
16
18
|
## How to capture on KDE Plasma
|
|
17
19
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
pkgbase = refrain
|
|
2
2
|
pkgdesc = Discord Rich Presence for Apple Music on Linux
|
|
3
|
-
pkgver = 0.2.
|
|
3
|
+
pkgver = 0.2.6
|
|
4
4
|
pkgrel = 1
|
|
5
5
|
url = https://github.com/Rockykln/refrain
|
|
6
6
|
arch = any
|
|
@@ -14,7 +14,7 @@ pkgbase = refrain
|
|
|
14
14
|
depends = python-dbus
|
|
15
15
|
depends = pyside6
|
|
16
16
|
optdepends = python-gobject: enables MPRIS-server publication so Plasma media controls reach Refrain
|
|
17
|
-
source = refrain-0.2.
|
|
18
|
-
sha256sums =
|
|
17
|
+
source = refrain-0.2.6.tar.gz::https://github.com/Rockykln/refrain/archive/v0.2.6.tar.gz
|
|
18
|
+
sha256sums = d5d99a19be2b7414e7d7d1324c7893d2a227a60d15c14969d69102f9d2850294
|
|
19
19
|
|
|
20
20
|
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.7
|
|
5
5
|
pkgrel=1
|
|
6
6
|
pkgdesc="Discord Rich Presence for Apple Music on Linux"
|
|
7
7
|
arch=('any')
|
|
@@ -23,7 +23,7 @@ makedepends=(
|
|
|
23
23
|
'python-wheel'
|
|
24
24
|
)
|
|
25
25
|
source=("$pkgname-$pkgver.tar.gz::https://github.com/Rockykln/refrain/archive/v$pkgver.tar.gz")
|
|
26
|
-
sha256sums=('
|
|
26
|
+
sha256sums=('d5d99a19be2b7414e7d7d1324c7893d2a227a60d15c14969d69102f9d2850294')
|
|
27
27
|
|
|
28
28
|
build() {
|
|
29
29
|
cd "$pkgname-$pkgver"
|
|
@@ -51,6 +51,38 @@
|
|
|
51
51
|
<content_rating type="oars-1.1" />
|
|
52
52
|
|
|
53
53
|
<releases>
|
|
54
|
+
<release version="0.2.7" date="2026-05-07">
|
|
55
|
+
<description>
|
|
56
|
+
<p>
|
|
57
|
+
Tray menu polish + Discord-RPC reliability. Every tray
|
|
58
|
+
action now carries an icon (theme-matched on Plasma /
|
|
59
|
+
GNOME / Breeze), the song-info rows render in normal text
|
|
60
|
+
colour with proper icons instead of greyed-out indented
|
|
61
|
+
rows, and middle-clicking the tray icon toggles
|
|
62
|
+
play/pause. Discord handshake errors (e.g. "User logged
|
|
63
|
+
out", "Invalid Client ID") are now caught explicitly
|
|
64
|
+
instead of crash-logged as unexpected exceptions, with a
|
|
65
|
+
longer back-off so Refrain doesn't retry tightly while
|
|
66
|
+
you're signing back in. The Settings → Updates
|
|
67
|
+
"Latest release notes" pane populates on every restart
|
|
68
|
+
regardless of the 24-hour update-check cooldown.
|
|
69
|
+
</p>
|
|
70
|
+
</description>
|
|
71
|
+
</release>
|
|
72
|
+
<release version="0.2.6" date="2026-05-07">
|
|
73
|
+
<description>
|
|
74
|
+
<p>
|
|
75
|
+
Localization expansion. Refrain now ships full UI
|
|
76
|
+
translations for 8 additional languages — Spanish,
|
|
77
|
+
French, Portuguese, Italian, Russian, Polish, Japanese,
|
|
78
|
+
and Simplified Chinese — covering an additional ~3
|
|
79
|
+
billion native speakers worldwide on top of English and
|
|
80
|
+
German. Also fixes the empty "Latest release notes" pane
|
|
81
|
+
that pipx-installed v0.2.5 showed on the Updates tab
|
|
82
|
+
when no auto-check had fetched the body yet.
|
|
83
|
+
</p>
|
|
84
|
+
</description>
|
|
85
|
+
</release>
|
|
54
86
|
<release version="0.2.5" date="2026-05-07">
|
|
55
87
|
<description>
|
|
56
88
|
<p>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.7"
|