Yandex Music: rotor session API, Wave Modes, user presets, library sync improvements#3606
Conversation
🔒 Dependency Security Report📦 Modified Dependencies
|
| Name | Skip Reason |
|---|---|
| torch | Dependency not found on PyPI and could not be audited: torch (2.11.0+cpu) |
| torchaudio | Dependency not found on PyPI and could not be audited: torchaudio (2.11.0+cpu) |
| ✅ No known vulnerabilities found |
Automated Security Checks
- ✅ Vulnerability Scan: Passed - No known vulnerabilities
- ✅ Trusted Sources: All packages have verified source repositories
- ✅ Typosquatting Check: No suspicious package names detected
- ✅ License Compatibility: All licenses are OSI-approved and compatible
- ✅ Supply Chain Risk: Passed - packages appear mature and maintained
Manual Review
Maintainer approval required:
- I have reviewed the changes above and approve these dependency updates
To approve: Comment /approve-dependencies or manually add the dependencies-reviewed label.
e858df6 to
06b99c4
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds a QR-based authentication flow to the Yandex Music provider and introduces startup-time token validation/refresh using a stored session token (x_token), aiming to reduce manual re-authentication and improve resilience to token expiry.
Changes:
- Added a new
yandex_auth.pymodule implementing Yandex Passport QR login andx_token→music_tokenexchange/refresh. - Updated provider initialization to attempt music-token login first, then refresh from
x_tokenwhen needed, clearing invalid credentials appropriately. - Adjusted streaming range-window EOF detection logic to account for AES block alignment re-downloads.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
music_assistant/providers/yandex_music/yandex_auth.py |
Implements QR auth flow and token refresh helper(s) using a dedicated aiohttp session/cookie jar. |
music_assistant/providers/yandex_music/__init__.py |
Adds QR login action, “remember session” toggle, and hidden storage for x_token in config flow. |
music_assistant/providers/yandex_music/provider.py |
Adds cascading token validation and refresh-from-x_token logic during handle_async_init(). |
music_assistant/providers/yandex_music/streaming.py |
Fixes premature EOF detection by correcting received byte accounting when reconnecting off AES boundaries. |
music_assistant/providers/yandex_music/constants.py |
Adds new config/action keys for QR auth and session token persistence. |
music_assistant/providers/yandex_music/api_client.py |
Reduces risk of credential leakage by removing exc_info=True in catch-all logging. |
Add Yandex Passport QR code authentication as the primary login method, replacing manual token entry (kept as advanced fallback). - QR auth flow via YandexQRAuth class with dedicated aiohttp session - Cascading token validation at startup: music_token → x_token refresh → clear - User-configurable "Remember session" toggle for x_token storage - Mobile User-Agent for HAOS compatibility with Yandex Passport - Multi-pattern CSRF extraction for different Yandex page formats - Remove exc_info=True logging that could leak token values Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
06b99c4 to
92a6c86
Compare
|
I see this code is now being duplicated across 3-4 providers. Would it be an idea to create a (QR) Auth package that can be reused? |
Good idea, I was actually thinking about the same thing. Here's the plan: Extract
All 3 Yandex providers ( Would this approach work, or do you have a different structure in mind? |
You probably know this better than I do. Another solution would be to create a Yandex pip package that includes more than just the QR Auth, so we can also get rid of the duplicate logic between the KION / Yandex providers? What would be a convenient point of extracting logic into a separate package for reuse? |
Good point about a separate package. |
|
Could you please fix all the lint / test issue? Please mark as ready when you are done and want me to have another look? |
Removed autogenerated warning and updated package list.
…all.py Previous commits on this branch stripped the file to 1 line while addressing the dependency-approval bot, which broke CI — tests fail with ModuleNotFoundError: No module named 'hass_client' at conftest import, because hass_client is a core MA dependency shipped via this file (it is not manually removable without breaking the whole suite). Regenerated from pyproject.toml + provider manifests using the canonical scripts/gen_requirements_all.py. yandex-music==3.0.0 and ya-passport-auth==1.3.0 are now correctly included alongside the rest of the core dependencies — they are consumed by music_assistant.providers.yandex_music and must be installable.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This reverts commit 093128a.
…istic output os.listdir order depends on filesystem (inode order on Linux ext4 vs alphabetic on macOS APFS), making requirements_all.txt regeneration non-deterministic across platforms. When two providers declare different versions of the same package, the order decided which one wins — leading to CI lint failures that flipped between runs. Wrapping the outer listdir in sorted() makes the result stable. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…deterministic output" This reverts commit e9ce07a.
…generation Temporary alignment until music-assistant#3804 (sorted gen_requirements_all) lands and makes the choice deterministic. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ain) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Syncs the Yandex Music provider in MA core with the out-of-tree provider repo
trudenboy/ma-provider-yandex-musicthrough 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)
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-livedradioSessionIdonPOST /rotor/session/new(together withincludeWaveModel=true+interactive=trueflags) that anchors all subsequent feedback, so wave state now survives across browse / recommendations / play entry points instead of losing a per-requestbatch_idon 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_addon a wave track also fires a rotorlikeevent on the active session (so this session adapts, not just the next one).on_playedkicks off a background prefetch of the next wave batch intowave.prefetchedso 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_dynamicon autoplaylists. Feed-generated playlists (Playlist of the Day / DejaVu / Premiere / Missed Likes) now carryis_dynamic=Trueso MA doesn't long-cache their content — Yandex regenerates them on a schedule.Browse fixes that landed in the same release.
<prov>://collection/tracks) so the back button returns to the listing instead of the provider root; dispatcher still delegatescollection/<sub>to core's default library handler.item_idunderscore form so play-time reconstruction works,pathslash form so back-nav works).full_model— switched to collectingtrack_ids fromitem_idand batch-hydrating viaget_tracks. 42 tracks returned where before 0.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). Album/audiobook split now respects the requestedlimitper media-type bucket, not per raw API page.v3.3.0 — audiobook progress sync & discovery (#115)
on_played/on_streamedpush chapter position via a newplay_audioAPI wrapper so the user's Yandex apps stay in sync with MA's resume point.parse_audiobookpropagatesalbum.listening_finished→Audiobook.fully_played.MediaType.AUDIOBOOKmaps to Yandexalbumsearch; results split byclassify_albuminto albums / audiobooks. Dedup soALBUM + AUDIOBOOKissues a single API call.MusicProvider.browse→ existingget_library_audiobooks/get_library_podcastsgenerators.CancelledErrorfailure from chapter-map resolution;_audiobook_progress_pointhelper handles EOF (reports end of last chapter, not start) and clampstrack_length_seconds ≥ 1/ offset into[0, track_length]. Caches (_audiobook_chapter_cache,_audiobook_play_ids) cleared onon_streamedandunload.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
meta_type/type._get_liked_albums_cacheddedupes 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.ya-passport-authv1.3.0./get-file-infounified for all quality tiers with native byte-range seek.Dependencies
yandex-music==3.0.0ya-passport-auth==1.3.0Documentation
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.
Review history on the upstream repo
PR #118 went through three rounds of Copilot review that caught:
NetworkErrorbefore_call_with_retrycould see it (transient retry never fired);_fetch_similar_tracks_for_seedcreating an unbounded_wave_statesentry per seed (cached, but stateful) — now stateless;_prefetch_rotor_sessionmutating shared state between lock phases — now directrotor_session_trackswithout going through the session helper;_get_my_wave_recommendations,get_rotor_station_tracks,_prefetch_rotor_session;provider/presets.py.All 14 review threads resolved before merge. Also addressed Marvin Schenkel's review on #3234 about providers not toggling queue UX settings on the user's behalf —
_ensure_dont_stop_the_music/queue.radio_sourcewrites were removed.Test plan
MediaType.AUDIOBOOK.