Skip to content

docs(yandex): Media Types, My Wave / Wave Modes / My Presets, Device Flow auth#629

Merged
OzGav merged 1 commit intomusic-assistant:betafrom
trudenboy:docs/yandex-v3.4
May 7, 2026
Merged

docs(yandex): Media Types, My Wave / Wave Modes / My Presets, Device Flow auth#629
OzGav merged 1 commit intomusic-assistant:betafrom
trudenboy:docs/yandex-v3.4

Conversation

@trudenboy
Copy link
Copy Markdown
Contributor

@trudenboy trudenboy commented Apr 23, 2026

Summary

Updates the Yandex Music source documentation to reflect features shipped in the provider after #498 — specifically ma-provider-yandex-music versions v3.0 through v3.4.0 (landing in core via music-assistant/server#3606).

Changes

Features table

Feature Before After
Media Types Supported Artists, Albums, Tracks, Playlists + Podcasts, Audiobooks
Login Method Token Device Flow / QR Code / Token

Other section — added

  • My Wave personalised radio with long-lived rotor session (rotor signals like/dislike, skips and full plays back to the recommendation algorithm)
  • Wave Modes — 11 one-click presets (Discover / Favorites / Popular · Calm / Active / Fun / Sad · Russian / Non-Russian / Without Words)
  • My Presets — user-saved named combinations of diversity, mood and language, launchable from Browse
  • Radio Mode from a wave track continues the same Yandex-curated session instead of branching into an unrelated similar-tracks stream
  • Podcasts are synced, browsable with episodes, and stream natively
  • Audiobooks are synced, browsable with chapters, stream natively, and resume points round-trip between Music Assistant and Yandex's own apps

Configuration section — rewritten

Three authentication methods now, each as its own subsection. Device Flow is presented first as the recommended path (non-interactive, refreshable). QR second. Manual OAuth token stays as advanced/fallback.

Settings — expanded

  • Added Remember session toggle
  • Added My Wave custom presets builder (the advanced-settings UI that backs the My Presets browse folder)

Known Issues / Notes — added

  • Clarified that Yandex Listening History is written only from an Ynison WebSocket session — tracks played through MA's Yandex Music source do not appear in Yandex's own history feed unless the companion Yandex Music Connect (Ynison) plugin is also installed. This came up repeatedly during v3.4 rollout.
  • Split the "token expires and needs refresh" note: only applies to manual OAuth tokens; Device Flow / QR setups auto-refresh.

Related

…Flow auth

Brings the Yandex Music source page in line with ma-provider-yandex-music
v3.4.0 (https://github.com/trudenboy/ma-provider-yandex-music, shipped
via music-assistant/server#3606):

- Media Types adds Podcasts and Audiobooks (v3.1–v3.3 landed those
  library types with library sync, browse, streaming and in v3.3
  cross-device resume-point sync for audiobooks).
- Login Method now Device Flow / QR Code / Token (v3.0 added Device
  Flow and QR; the old single-token path stays as advanced).
- Other section calls out the v3.4 rotor-session work: long-lived My
  Wave session, the 11 one-click Wave Modes, user-defined My Presets,
  and the behaviour where Radio Mode continues the active wave
  instead of branching.
- Configuration section rewritten around the three auth methods.
- Settings lists the new 'Remember session' toggle and the wave
  presets builder.
- Known Issues notes that Yandex Listening History is only written
  from an Ynison WebSocket session, so regular MA playback isn't
  visible there without the companion plugin — avoids the FAQ that
  kept coming up during v3.4 rollout.
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 23, 2026

Deploy Preview for ohf-music-assistant ready!

Name Link
🔨 Latest commit f3eb427
🔍 Latest deploy log https://app.netlify.com/projects/ohf-music-assistant/deploys/69ea0a7297434d0008e9d848
😎 Deploy Preview https://deploy-preview-629--ohf-music-assistant.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

MarvinSchenkel pushed a commit to music-assistant/server that referenced this pull request Apr 28, 2026
…nc improvements (#3606)

Syncs the Yandex Music provider in MA core with the out-of-tree provider
repo
[`trudenboy/ma-provider-yandex-music`](https://github.com/trudenboy/ma-provider-yandex-music)
through **v3.4.0**.

The last in-tree version was pre-v3. This PR folds in several release
lines worth of functional, infrastructure and UX work.

## Highlights

### v3.4.0 — rotor session API + Wave Modes + user presets
([#118](trudenboy/ma-provider-yandex-music#118))

**Rotor session API migration.** My Wave, tagged stations (genre:rock /
mood:calm / activity:workout / epoch:*) and track-seed similar-tracks
all moved from the legacy `/rotor/station/{id}/*` endpoint to Yandex's
newer `/rotor/session/{new,tracks,feedback}`. The server returns a
long-lived `radioSessionId` on `POST /rotor/session/new` (together with
`includeWaveModel=true` + `interactive=true` flags) that anchors all
subsequent feedback, so wave state now survives across browse /
recommendations / play entry points instead of losing a per-request
`batch_id` on every entry.

**Wave Modes browse folder (11 playable presets).** diversity (Discover
/ Favorites / Popular), moodEnergy (Calm / Active / Fun / Sad) and
language (Russian / Non-Russian / Without Words). Each is a separate
playable BrowseFolder that creates a rotor session with the matching
seed setting. Composite item_ids encode the preset, so rotor feedback
lands on the right session.

**User-defined wave presets** — advanced settings expose a small
builder: name + three dropdowns + Save / Delete action buttons. Presets
are persisted in a hidden JSON config key and surface as playable
folders under Radio → My Presets. Unlimited count, no hand-editing of
JSON.

**Rotor likes / prefetch.** `library_add` on a wave track also fires a
rotor `like` event on the active session (so _this_ session adapts, not
just the next one). `on_played` kicks off a background prefetch of the
next wave batch into `wave.prefetched` so MA's DSTM refill (if the user
enables it) gets Yandex-curated tracks without an extra round-trip. The
provider does **not** toggle DSTM — that stays a user decision.

**`is_dynamic` on autoplaylists.** Feed-generated playlists (Playlist of
the Day / DejaVu / Premiere / Missed Likes) now carry `is_dynamic=True`
so MA doesn't long-cache their content — Yandex regenerates them on a
schedule.

**Browse fixes that landed in the same release.**
- Collection child paths are nested (`<prov>://collection/tracks`) so
the back button returns to the listing instead of the provider root;
dispatcher still delegates `collection/<sub>` to core's default library
handler.
- Wave Modes / My Presets back-navigation — same dual-form path contract
(`item_id` underscore form so play-time reconstruction works, `path`
slash form so back-nav works).
- Listening History was empty because MarshalX stopped populating
`full_model` — switched to collecting `track_id`s from `item_id` and
batch-hydrating via `get_tracks`. 42 tracks returned where before 0.
- AI Wave Sets / Featured Waves rendered empty sub-folders because the
payload uses camelCase (`stationId`, `compactImageUrl`) while the code
asked for snake_case. Both forms now accepted.

### v3.3.1 — search fix

`fix(search): apply limit per bucket after classify_album`
([#116](trudenboy/ma-provider-yandex-music#116)).
Album/audiobook split now respects the requested `limit` per media-type
bucket, not per raw API page.

### v3.3.0 — audiobook progress sync & discovery
([#115](trudenboy/ma-provider-yandex-music#115))

- **Progress sync to Yandex.** `on_played` / `on_streamed` push chapter
position via a new `play_audio` API wrapper so the user's Yandex apps
stay in sync with MA's resume point. `parse_audiobook` propagates
`album.listening_finished` → `Audiobook.fully_played`.
- **Audiobook search.** `MediaType.AUDIOBOOK` maps to Yandex `album`
search; results split by `classify_album` into albums / audiobooks.
Dedup so `ALBUM + AUDIOBOOK` issues a single API call.
- **Collection browse.** *My Audiobooks* / *My Podcasts* sub-folders (RU
+ EN) routed through base `MusicProvider.browse` → existing
`get_library_audiobooks` / `get_library_podcasts` generators.
- **Robustness.** Advisory progress path swallows any
non-`CancelledError` failure from chapter-map resolution;
`_audiobook_progress_point` helper handles EOF (reports end of last
chapter, not start) and clamps `track_length_seconds ≥ 1` / offset into
`[0, track_length]`. Caches (`_audiobook_chapter_cache`,
`_audiobook_play_ids`) cleared on `on_streamed` and `unload`.

### v3.2.x — audiobook streaming

- `fix(audiobook): wire seek through can_seek and bound chapter
failures` (v3.2.1).
- `feat(audiobook): stream audiobooks via chapter concatenation`
(v3.2.0) — CUSTOM StreamType iterating per-chapter byte streams with
seek translation + failure tolerance.

### v3.1.x — library classifier + auth fixes

- Podcast + audiobook classifier (Phase 1): liked albums routed to music
/ podcast / audiobook via `meta_type` / `type`.
- Short-lived `_get_liked_albums_cached` dedupes the three library
syncs.
- `fix(auth)` series: clear stale refresh_token on QR re-auth, wrap
transient Passport errors, don't clear creds on transient failures.

### v3.0.x — yandex-music v3 migration

- `yandex-music[async]` v2 → v3.0.0, parsers updated for stricter
typing.
- Device Flow auth added alongside QR; auto-refresh via
`ya-passport-auth` v1.3.0.
- `/get-file-info` unified for all quality tiers with native byte-range
seek.
- Pinned items + Listening History browse folders.

## Dependencies

- `yandex-music==3.0.0`
- `ya-passport-auth==1.3.0`


## Documentation

Docs PR adding the v3.4 feature coverage to music-assistant.io (Wave
Modes / My Presets / Device Flow auth, podcast & audiobook support,
Known Issues note about Listening History):
[music-assistant/music-assistant.io#629](music-assistant/music-assistant.io#629).

## Review history on the upstream repo

PR
[#118](trudenboy/ma-provider-yandex-music#118)
went through three rounds of Copilot review that caught:
- rotor session request swallowing `NetworkError` before
`_call_with_retry` could see it (transient retry never fired);
- `_fetch_similar_tracks_for_seed` creating an unbounded `_wave_states`
entry per seed (cached, but stateful) — now stateless;
- `_prefetch_rotor_session` mutating shared state between lock phases —
now direct `rotor_session_tracks` without going through the session
helper;
- locking gaps in `_get_my_wave_recommendations`,
`get_rotor_station_tracks`, `_prefetch_rotor_session`;
- duplicated JSON preset parser in two files — extracted to
`provider/presets.py`.

All 14 review threads resolved before merge. Also addressed Marvin
Schenkel's review on
[#3234](#3234) about
providers not toggling queue UX settings on the user's behalf —
`_ensure_dont_stop_the_music` / `queue.radio_source` writes were
removed.

## Test plan

- [x] Provider unit tests green — 254 passed covering rotor session
plumbing, wave state machine, preset routing, prefetch, rotor feedback,
browse listings, audiobook progress, search routing, auth, parsers.
- [x] ruff / mypy clean.
- [x] Live validation against a real Yandex account in Docker — session
API round-trips verified for user:onyourwave / track:{id} / genre:rock /
mood:calm / activity:workout / epoch:eighties; feedback + pagination;
browse back-nav; history hydration.
- [ ] Core-side: provider loads; library sync still works; My Wave
plays; Wave Modes / My Presets render and play; audiobook position
round-trips with Yandex mobile app; search returns audiobooks under
`MediaType.AUDIOBOOK`.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
fionn-r pushed a commit to fionn-r/music-assistant-server that referenced this pull request May 4, 2026
…nc improvements (music-assistant#3606)

Syncs the Yandex Music provider in MA core with the out-of-tree provider
repo
[`trudenboy/ma-provider-yandex-music`](https://github.com/trudenboy/ma-provider-yandex-music)
through **v3.4.0**.

The last in-tree version was pre-v3. This PR folds in several release
lines worth of functional, infrastructure and UX work.

## Highlights

### v3.4.0 — rotor session API + Wave Modes + user presets
([music-assistant#118](trudenboy/ma-provider-yandex-music#118))

**Rotor session API migration.** My Wave, tagged stations (genre:rock /
mood:calm / activity:workout / epoch:*) and track-seed similar-tracks
all moved from the legacy `/rotor/station/{id}/*` endpoint to Yandex's
newer `/rotor/session/{new,tracks,feedback}`. The server returns a
long-lived `radioSessionId` on `POST /rotor/session/new` (together with
`includeWaveModel=true` + `interactive=true` flags) that anchors all
subsequent feedback, so wave state now survives across browse /
recommendations / play entry points instead of losing a per-request
`batch_id` on every entry.

**Wave Modes browse folder (11 playable presets).** diversity (Discover
/ Favorites / Popular), moodEnergy (Calm / Active / Fun / Sad) and
language (Russian / Non-Russian / Without Words). Each is a separate
playable BrowseFolder that creates a rotor session with the matching
seed setting. Composite item_ids encode the preset, so rotor feedback
lands on the right session.

**User-defined wave presets** — advanced settings expose a small
builder: name + three dropdowns + Save / Delete action buttons. Presets
are persisted in a hidden JSON config key and surface as playable
folders under Radio → My Presets. Unlimited count, no hand-editing of
JSON.

**Rotor likes / prefetch.** `library_add` on a wave track also fires a
rotor `like` event on the active session (so _this_ session adapts, not
just the next one). `on_played` kicks off a background prefetch of the
next wave batch into `wave.prefetched` so MA's DSTM refill (if the user
enables it) gets Yandex-curated tracks without an extra round-trip. The
provider does **not** toggle DSTM — that stays a user decision.

**`is_dynamic` on autoplaylists.** Feed-generated playlists (Playlist of
the Day / DejaVu / Premiere / Missed Likes) now carry `is_dynamic=True`
so MA doesn't long-cache their content — Yandex regenerates them on a
schedule.

**Browse fixes that landed in the same release.**
- Collection child paths are nested (`<prov>://collection/tracks`) so
the back button returns to the listing instead of the provider root;
dispatcher still delegates `collection/<sub>` to core's default library
handler.
- Wave Modes / My Presets back-navigation — same dual-form path contract
(`item_id` underscore form so play-time reconstruction works, `path`
slash form so back-nav works).
- Listening History was empty because MarshalX stopped populating
`full_model` — switched to collecting `track_id`s from `item_id` and
batch-hydrating via `get_tracks`. 42 tracks returned where before 0.
- AI Wave Sets / Featured Waves rendered empty sub-folders because the
payload uses camelCase (`stationId`, `compactImageUrl`) while the code
asked for snake_case. Both forms now accepted.

### v3.3.1 — search fix

`fix(search): apply limit per bucket after classify_album`
([music-assistant#116](trudenboy/ma-provider-yandex-music#116)).
Album/audiobook split now respects the requested `limit` per media-type
bucket, not per raw API page.

### v3.3.0 — audiobook progress sync & discovery
([music-assistant#115](trudenboy/ma-provider-yandex-music#115))

- **Progress sync to Yandex.** `on_played` / `on_streamed` push chapter
position via a new `play_audio` API wrapper so the user's Yandex apps
stay in sync with MA's resume point. `parse_audiobook` propagates
`album.listening_finished` → `Audiobook.fully_played`.
- **Audiobook search.** `MediaType.AUDIOBOOK` maps to Yandex `album`
search; results split by `classify_album` into albums / audiobooks.
Dedup so `ALBUM + AUDIOBOOK` issues a single API call.
- **Collection browse.** *My Audiobooks* / *My Podcasts* sub-folders (RU
+ EN) routed through base `MusicProvider.browse` → existing
`get_library_audiobooks` / `get_library_podcasts` generators.
- **Robustness.** Advisory progress path swallows any
non-`CancelledError` failure from chapter-map resolution;
`_audiobook_progress_point` helper handles EOF (reports end of last
chapter, not start) and clamps `track_length_seconds ≥ 1` / offset into
`[0, track_length]`. Caches (`_audiobook_chapter_cache`,
`_audiobook_play_ids`) cleared on `on_streamed` and `unload`.

### v3.2.x — audiobook streaming

- `fix(audiobook): wire seek through can_seek and bound chapter
failures` (v3.2.1).
- `feat(audiobook): stream audiobooks via chapter concatenation`
(v3.2.0) — CUSTOM StreamType iterating per-chapter byte streams with
seek translation + failure tolerance.

### v3.1.x — library classifier + auth fixes

- Podcast + audiobook classifier (Phase 1): liked albums routed to music
/ podcast / audiobook via `meta_type` / `type`.
- Short-lived `_get_liked_albums_cached` dedupes the three library
syncs.
- `fix(auth)` series: clear stale refresh_token on QR re-auth, wrap
transient Passport errors, don't clear creds on transient failures.

### v3.0.x — yandex-music v3 migration

- `yandex-music[async]` v2 → v3.0.0, parsers updated for stricter
typing.
- Device Flow auth added alongside QR; auto-refresh via
`ya-passport-auth` v1.3.0.
- `/get-file-info` unified for all quality tiers with native byte-range
seek.
- Pinned items + Listening History browse folders.

## Dependencies

- `yandex-music==3.0.0`
- `ya-passport-auth==1.3.0`


## Documentation

Docs PR adding the v3.4 feature coverage to music-assistant.io (Wave
Modes / My Presets / Device Flow auth, podcast & audiobook support,
Known Issues note about Listening History):
[music-assistant/music-assistant.io#629](music-assistant/music-assistant.io#629).

## Review history on the upstream repo

PR
[music-assistant#118](trudenboy/ma-provider-yandex-music#118)
went through three rounds of Copilot review that caught:
- rotor session request swallowing `NetworkError` before
`_call_with_retry` could see it (transient retry never fired);
- `_fetch_similar_tracks_for_seed` creating an unbounded `_wave_states`
entry per seed (cached, but stateful) — now stateless;
- `_prefetch_rotor_session` mutating shared state between lock phases —
now direct `rotor_session_tracks` without going through the session
helper;
- locking gaps in `_get_my_wave_recommendations`,
`get_rotor_station_tracks`, `_prefetch_rotor_session`;
- duplicated JSON preset parser in two files — extracted to
`provider/presets.py`.

All 14 review threads resolved before merge. Also addressed Marvin
Schenkel's review on
[music-assistant#3234](music-assistant#3234) about
providers not toggling queue UX settings on the user's behalf —
`_ensure_dont_stop_the_music` / `queue.radio_source` writes were
removed.

## Test plan

- [x] Provider unit tests green — 254 passed covering rotor session
plumbing, wave state machine, preset routing, prefetch, rotor feedback,
browse listings, audiobook progress, search routing, auth, parsers.
- [x] ruff / mypy clean.
- [x] Live validation against a real Yandex account in Docker — session
API round-trips verified for user:onyourwave / track:{id} / genre:rock /
mood:calm / activity:workout / epoch:eighties; feedback + pagination;
browse back-nav; history hydration.
- [ ] Core-side: provider loads; library sync still works; My Wave
plays; Wave Modes / My Presets render and play; audiobook position
round-trips with Yandex mobile app; search returns audiobooks under
`MediaType.AUDIOBOOK`.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@OzGav OzGav merged commit 95768d0 into music-assistant:beta May 7, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants