- Python 87.3%
- Dockerfile 12.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| patches | ||
| tests | ||
| .upstream-version | ||
| Dockerfile | ||
| README.md | ||
Music Assistant
The official Music Assistant image (ghcr.io/music-assistant/server) with the
patches in patches/ applied. Published as
forgejo.mcy.space/music-assistant/music-assistant:<major.minor>.<run>.
Patches
jellyfin-playback-reporting.patch
Upstream's Jellyfin provider never tells Jellyfin what was played, so plays from Music Assistant are missing from Jellyfin's play counts and from anything that learns from them (the Harmonie plugin's mixes and "On Repeat" lists).
The patch implements the provider's on_played hook, the same hook the Plex and
Emby providers use. Each track becomes a Jellyfin playback session, reported
with the provider's existing Jellyfin login:
| Music Assistant | Jellyfin call |
|---|---|
| first report for a track | POST /Sessions/Playing |
| every 30 s while playing | POST /Sessions/Playing/Progress |
| played to the end, stopped or skipped | POST /Sessions/Playing/Stopped with the position |
| marked unplayed in the UI | DELETE /UserPlayedItems/{id} |
Jellyfin then sets the play count, last-played date and resume position itself, and server plugins see the playback events as they would for any other client. A failed report is logged as a warning and never interrupts playback.
The patch can go away once upstream's provider reports playback itself.
jellyfin-sync-favorites.patch
Upstream's provider reads Jellyfin favorites but never writes them, so a favorite set in Music Assistant stays there and Jellyfin, Symfonium, Harmonie and the ListenBrainz plugin never see it.
The patch declares the provider able to edit favorites for tracks, albums,
artists and playlists, and implements set_favorite: favoriting sends
POST /UserFavoriteItems/{id}, unfavoriting sends DELETE. It applies on
top of the playback patch and reuses its request helper, so a failure is
logged, never raised.
A favorite change for the track that is playing is held back and sent right after that track's stopped report. Jellyfin saves each progress and stopped report with a copy of the track's user data taken when playback started, so a favorite sent mid-track is overwritten with the old value at the next progress report, within 30 seconds. Held back, the change lands after the last report of the session. Only the latest change is sent, so liking and then unliking a track during playback sends nothing but the unlike.
Build
.forgejo/workflows/build.yml runs daily, on a push to main and by hand:
- Reads the latest stable release from GitHub (pre-releases and nightlies are
ignored). A scheduled run builds only when it differs from
.upstream-version. - Builds
FROM ghcr.io/music-assistant/server:<version>and applies the patches withpatch --fuzz=0. A patch that no longer applies exactly fails the build, so a broken patch never ships silently. - Runs
tests/inside the built image. Each test drives the patched provider against a fake Jellyfin server and checks every request sent. - Pushes
<major.minor>.<run number>and records the version in.upstream-version.
Keel follows the tag with policy patch and approval. A new upstream minor
release (for example 2.11) starts a new series, which needs a manual bump in the
Deployment.
Secret
REGISTRY_TOKEN (repo Actions secret) is a Forgejo PAT with write:package,
read:package and write:repository: it pushes the image and the
.upstream-version commit.
Building locally
podman build --build-arg MA_VERSION=2.10.9 -t music-assistant:test .
for t in tests/*.py; do podman run --rm -i --entrypoint /app/venv/bin/python music-assistant:test - < $t; done
Updating the patch
Edit the provider from the upstream tag and regenerate the diff with paths
relative to site-packages (a/music_assistant/..., b/music_assistant/...),
then build and run the tests as above.