Skip to content

feat(mpris): make the player rows tell you which is which - #39

Open
Zakkaus wants to merge 3 commits into
locez:mainfrom
Zakkaus:feat/player-picker-detail
Open

feat(mpris): make the player rows tell you which is which#39
Zakkaus wants to merge 3 commits into
locez:mainfrom
Zakkaus:feat/player-picker-detail

Conversation

@Zakkaus

@Zakkaus Zakkaus commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Builds on #22. Review only feat(mpris): make the player rows tell you which is which.

Problem, from a real desktop

With Firefox and Chrome both playing, #22's picker lists four rows:

Chrome
Mozilla firefox
Mozilla Firefox
Google Chrome

Each browser publishes its own MPRIS endpoint, and Plasma Browser Integration bridges the same tab, so half the list is duplicates — and two of the names differ only in one letter's case. There is no way to tell which row is the music you want.

Fix: say what each endpoint is doing

自動選擇
Chrome · 播放中 · 下山 (梦幻西游方寸山门派曲)
Mozilla firefox · 播放中 · 大石碎胸口
自動 · Mozilla Firefox · 播放中 · 大石碎胸口 - 万能青年旅店
Google Chrome · 播放中 · 下山 (梦幻西游方寸山门派曲) - 要不要买菜

Identity, playback status, and the loaded track. Now the two Firefox rows are visibly the same song and the two Chrome rows are visibly the other one, so the choice is obvious. The row the automatic selection would pick is marked, so a user who does not want to choose can see the default is already sensible.

Metadata and PlaybackStatus ride on the D-Bus round trip that already fetched Identity, so this costs no extra traffic.

What it deliberately does not do

No deduplication. Two endpoints for one browser are a real choice, not noise. The bridge usually reports a cleaner artist — that is why the automatic ranking prefers it, as the comment above _selection_score explains — but "usually" is not "always", and hiding the wrong one would leave the user with only the broken option.

Two defects fixed while building it

Status was untranslated. MPRIS reports it as a protocol constant, so the first version showed Playing inside an otherwise Chinese row.

A delegate segfaulted the suite. Eliding long rows with a QStyledItemDelegate looks right and is not: initStyleOption runs during sizeHint, where the option's widget can already be destroyed, and Qt crashes inside QWidget::style(). The backtrace was QComboBox::changeEventQListView::doItemsLayoutQStyledItemDelegate::sizeHintQWidget::style(). Eliding now happens where the row is built, which is our own string and needs no delegate.

Validation

  • QT_QPA_PLATFORM=offscreen uv run pytest — 386 passed, run three times for the segfault
  • uv run ruff check .
  • uv run ty check

Checked against a live session with four real endpoints, rendered in zh-Hant and en — the rows above are the actual output, not test data.

Follow-up: the marker read as a second automatic option

Verified with two browsers publishing on the session bus at once. The rows come out as intended — Firefox · Playing · アイドル by YOASOBI against Chromium · Paused · Bad Apple!! by nomico — but the row automatic selection had landed on was prefixed with the word automatic, directly under the row labelled Automatic. Two rows opening with the same word is the confusion this picker exists to remove, so the marker now says which row is the current one instead.

With Firefox and Chrome both playing, the picker listed four rows: Chrome,
Mozilla firefox, Mozilla Firefox, Google Chrome. Each browser publishes its own
MPRIS endpoint and Plasma Browser Integration bridges the same tab, so half the
list is duplicates and two of the names differ only in one letter's case. The
user cannot tell which row is their music.

Names cannot fix that, so the rows now say what each endpoint is actually doing:

    自動 · Mozilla Firefox · 播放中 · 大石碎胸口 - 万能青年旅店
    Google Chrome · 播放中 · 下山 (梦幻西游方寸山门派曲) - 要不要买菜

Identity, playback status, and the loaded track. The row the automatic choice
would pick is marked, so a user who does not want to choose can see the default
is already sensible. Reading `Metadata` and `PlaybackStatus` rides on the
round trip that already fetched `Identity`.

Nothing is deduplicated or hidden. Two endpoints for one browser are a real
choice — the bridge usually reports a cleaner artist, but not always, and hiding
the wrong one would leave the user with only the broken option.

MPRIS reports the status as a protocol constant, so it is translated for
display; a Chinese UI showing "Playing" was the first version of this.

Eliding happens where the row is built. A QStyledItemDelegate was the obvious
tool and the wrong one: its initStyleOption runs during sizeHint, where the
option's widget can already be destroyed, and Qt then crashes inside
QWidget::style(). That segfaulted the suite.
The row automatic selection is currently on was prefixed with the word
automatic, directly under a row labelled Automatic. Two rows starting
with the same word is exactly the confusion the picker was added to
remove. Mark it as the current one instead.
@Zakkaus
Zakkaus force-pushed the feat/player-picker-detail branch from b766b09 to 34a95b4 Compare August 16, 2026 07:54
The settings picker carried its own copy of the rule that decides which player
to follow, and the two copies ordered the last fallbacks the other way round:
with a Playing player reporting no metadata beside a Paused one that reports a
track, the row marked Current named the first while the poll followed the
second. The rule now lives in one method that both call, so they cannot drift
again, and the poll no longer accumulates five parallel candidate variables to
express it.

Identity and detail also shared one try, so a player that answered its name but
failed the optional status and metadata read vanished from the picker entirely
and could not be selected. The detail read now degrades to an empty status.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant