Bootstrap pyproject + asyncio package skeleton#3
Merged
Conversation
ed7ffb3 to
cbe5e11
Compare
First step of the autotools → pyproject + asyncio + dbus-fast refactor (see docs/refactor-asyncio-dbus-fast.md). Adds the mpdris2/ package with __init__, __main__, cli and a daemon stub that just awaits SIGTERM, the pyproject.toml + scripts/version.py + slim dev Makefile + babel.cfg, and tests/test_cli.py covering argparse and config loading. The old autotools build stays alongside until PR 4 cuts it over. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the dbus-python / GLib glue with dbus-fast ServiceInterface classes (MediaPlayer2 + MediaPlayer2Player) and an asyncio mpd.asyncio wrapper with exponential-backoff connect. The daemon's run() coroutine acquires the bus, watches MPD idle events on player/mixer/options/playlist, and translates state changes into PropertiesChanged emissions. Methods (Play/Pause/PlayPause/Stop/Next/Previous/Seek/SetPosition) and R/W properties (Volume / LoopStatus / Shuffle) round-trip to MPD; Metadata stays empty until PR 3 wires translate.py + cover.py. The mmkeys GNOME-SettingsDaemon grab is intentionally dropped — modern desktops route media keys through MPRIS2 directly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pure mapping of MPD currentsong fields onto MPRIS Metadata keys with dbus-fast Variants. DEFAULT_URL_HANDLERS lists the schemes that should pass through unchanged (http, https, …) so streamed tracks keep their scheme intact instead of being prefixed with the music library path. CDDA / CUE tracks frequently carry only ``albumartist``. MPRIS clients overwhelmingly read ``xesam:artist`` for the track-row artist column, so the translator mirrors albumArtist into artist when artist is missing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5-step pipeline tried in order, authoritative-first:
1. MPD ``readpicture`` — embedded art in the audio file (FLAC PICTURE,
ID3 APIC, …). Server-side parsing, works for local and remote MPD.
2. Filesystem regex match in the song's directory — uses the
configurable ``cover_regex`` (default catches cover.*, folder.*,
album.*, front.*) so non-standard names like ``folder.jpg`` are
resolved without any temp-file copy.
3. MPD ``albumart`` — MPD resolves cover.{png,jpg,jxl,webp} server-side
from the song's directory. Useful for remote MPD or standard-named
covers that step 2 missed.
4. CUE/cdda fallback — when ``song_file`` is virtual (cdda://, http://,
…) the audio file itself has no on-disk cover; look next to the
loaded .cue playlist instead (FS scan first, MPD ``albumart`` as a
remote fallback).
5. XDG cover cache — ``$XDG_CACHE_HOME/mpDris2/{artist}-{album}.jpg``
for the optional MusicBrainz / Cover Art Archive fallback (PR 5).
Capability flags from the MPD command list drive whether readpicture
or albumart are attempted at all (older MPDs ship neither). Bytes
returned by MPD land in /tmp/cover-*.{jpg,png,…} and the URI of that
file is exposed as ``mpris:artUrl``; the FS regex step returns the
matched file's URI directly (no copy).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
539dbce to
717a5e7
Compare
Thin libnotify wrapper built on dbus-fast — no PyGObject dependency. Notifier.notify(title, body, icon) fires (or replaces) a desktop bubble through the freedesktop Notifications interface; the daemon calls it on track change when the [Notify] section enables it. Tuning lives in two frozen dataclasses: * ``NotifierConfig(urgency, timeout)`` — display knobs forwarded to the notification server (urgency 0/1/2, expire_timeout in ms or -1 to defer to the server's default). * ``NotifyTemplates(summary, body, paused_summary, paused_body)`` — optional ``%placeholder%`` format strings consumed by ``format_template`` (placeholders: %album% %title% %id% %time% %timeposition% %date% %track% %disc% %artist% %albumartist% %composer% %genre% %file%). Empty templates fall back to built-in defaults; paused_* falls back to the playing template before the built-in default. ``_format_duration`` mirrors the original ``convert_timestamp`` so %time%/%timeposition% render as ``M:SS`` (or ``H:MM:SS`` past an hour). Unknown placeholders are left untouched rather than raising — friendlier when users typo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
refresh() now translates currentsong into MPRIS Metadata, resolves the artUrl asynchronously via CoverFinder, toggles CanSeek on mpris:length presence, and fires a libnotify bubble on track change. URL handlers are probed from MPD's urlhandlers command at startup so streams keep their scheme intact. The music library path is resolved from CLI / config / XDG and normalised to a file:// URL. [Notify] is preferred over the deprecated [Bling] section for the enable flag. Ported config keys from the historical mpDris2: * ``[Bling] cdprev`` — CD-like Previous: when elapsed >= 3 s, restart the current track via ``seekid`` instead of skipping to the previous one. * ``[Bling] notify_paused`` — also fire the bubble on track change while the player is paused (default off, matching the original). * ``[Notify] urgency`` / ``timeout`` — wired into ``NotifierConfig`` and forwarded to the notification server. * ``[Notify] summary`` / ``body`` / ``paused_summary`` / ``paused_body`` — ``%placeholder%`` templates wired into ``_build_track_notification`` (paused state falls back to the playing template, then the built-in default; the paused default appends "(Paused)" to the body and swaps the icon for ``media-playback-pause-symbolic``). Read with ``raw=True`` so configparser doesn't try to interpolate the literal ``%`` tokens. Closes feature parity with the original daemon minus mmkeys, which is intentionally dropped — modern desktops consume MPRIS directly for multimedia-key handling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Moves packaging to pybuild-plugin-pyproject so debian/rules no longer needs autoreconf; data files (systemd units, D-Bus activation, .desktop, example config, manpage) live under data/ and are installed via debian/mpdris2.install. CI is rebuilt on the snapclientmpris layout: lint (pip install + ruff + mypy + pytest + check-tag) then deb (in a debian:trixie container) then release + odio-apt-repo dispatch. i18n migrates from intltool to babel + msgfmt: po/fr.po and po/nl.po are preserved and re-merged against the new pot; mpdris2/locale/ is built as package_data so gettext finds the catalogs at runtime. cli.py binds the text domain; daemon.py uses ``from gettext import gettext as _`` for the notification strings. src/mpDris2.in.py and all autotools artefacts (configure.ac, autogen.sh, Makefile.am, INSTALL, src/, po/POTFILES.in, po/LINGUAS, debian/*.in) are gone. CLAUDE.md is rewritten to document the new layout. shell.nix swaps dbus-python + pygobject3 for dbus-fast and adds babel/ruff/mypy/ pytest for dev. .gitignore is trimmed to the patterns that still apply. Functional parity with the original daemon is preserved minus mmkeys (intentionally dropped — modern desktops route media keys through MPRIS2 directly). The systemd units keep ConditionUser=!root | !@System and Restart=always RestartSec=5. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Install: replace autogen/make with the apt.odio.love repository (primary) and a pipx/git fallback; drop the obsolete /usr/local prefix reference and the mutagen requirement (the asyncio package depends only on python-mpd2 and dbus-fast). - Configuration: document every ported key — [Library] cover_regex / cover_cache_dir, [Bling] notify / notify_paused / cdprev, [Notify] urgency / timeout / summary / body / paused_summary / paused_body — with the full %placeholder% set in a comment. - Note that [Bling] mmkeys is intentionally not ported: modern desktops (GNOME, KDE, sway) consume MPRIS directly for multimedia-key handling. - Add a short Architecture section pointing at docs/refactor-asyncio-dbus-fast.md. - Keep the Cover art resolution pipeline table introduced earlier. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
717a5e7 to
791de7e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First step of the autotools → pyproject + asyncio + dbus-fast refactor (see docs/refactor-asyncio-dbus-fast.md). Adds the mpdris2/ package with init, main, cli and a daemon stub that just awaits SIGTERM, the pyproject.toml + scripts/version.py + slim dev Makefile + babel.cfg, and tests/test_cli.py covering argparse and config loading. The old autotools build stays alongside until PR 4 cuts it over.