Skip to content

Bump mypy from 0.790 to 0.812#83

Merged
marcelveldt merged 1 commit intomasterfrom
dependabot/pip/mypy-0.812
Mar 3, 2021
Merged

Bump mypy from 0.790 to 0.812#83
marcelveldt merged 1 commit intomasterfrom
dependabot/pip/mypy-0.812

Conversation

@dependabot
Copy link
Contributor

@dependabot dependabot bot commented on behalf of github Feb 22, 2021

Bumps mypy from 0.790 to 0.812.

Commits

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [mypy](https://github.com/python/mypy) from 0.790 to 0.812.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](python/mypy@v0.790...v0.812)

Signed-off-by: dependabot[bot] <support@github.com>
@marcelveldt marcelveldt merged commit a512500 into master Mar 3, 2021
@marcelveldt marcelveldt deleted the dependabot/pip/mypy-0.812 branch March 3, 2021 14:59
MarvinSchenkel pushed a commit that referenced this pull request Feb 24, 2026
…rated collections, Extended recommendations etc (#3147)

* Yandex Music: Add configurable My Wave settings

Add 6 new configuration options for Yandex Music provider:
- My Wave maximum tracks (default: 150) - Control total number of tracks fetched
- My Wave batch count (default: 3) - Number of API calls for initial load
- Track details batch size (default: 50) - Batch size for track detail requests
- Discovery initial tracks (default: 5) - Initial display limit for Discover
- Browse initial tracks (default: 15) - Initial display limit for Browse
- Enable Discover (default: true) - Toggle recommendations on/off

Implemented duplicate protection for My Wave tracks using set-based tracking.
Recommendations now refresh every 60 seconds instead of 3 hours for fresher discoveries.

All new settings have sensible defaults that maintain current behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(yandex_music): add configurable base URL

Add Advanced setting for API base URL in Yandex Music provider,
allowing users to change the API endpoint if service updates their URL.

Changes:
- Add CONF_BASE_URL and DEFAULT_BASE_URL constants
- Add Advanced ConfigEntry for base_url in provider settings
- Update YandexMusicClient to accept base_url parameter
- Pass base_url from config to ClientAsync initialization

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(yandex_music): improve Liked Tracks with virtual playlist and sorting

This commit adds three major improvements to the Liked Tracks feature:

1. Reverse chronological sorting:
   - Liked tracks are now sorted by timestamp (most recent first)
   - Matches mobile app behavior for better UX
   - Applied automatically in get_liked_tracks() method

2. Browse folder visibility toggle:
   - Added CONF_ENABLE_LIKED_TRACKS_BROWSE config option
   - Allows hiding Liked Tracks folder from Browse section
   - Default: True (backward compatible)

3. Virtual playlist for Liked Tracks:
   - Added LIKED_TRACKS_PLAYLIST_ID virtual playlist
   - Appears in library playlists (similar to My Wave)
   - Supports full MA playlist features (radio, favorites, etc.)
   - Configurable via CONF_ENABLE_LIKED_TRACKS_PLAYLIST
   - Respects CONF_LIKED_TRACKS_MAX_TRACKS limit (default: 500)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(yandex_music): organize configuration settings with categories and advanced flags

Restructures the 16 Yandex Music provider configuration entries to improve UX:
- Groups My Wave settings (6 entries) in "my_wave" category
- Groups Liked Tracks settings (3 entries) in "liked_tracks" category
- Marks performance tuning settings as advanced (7 entries total)
- Maintains authentication/quality settings at top level

This reduces visible clutter from 16 to ~8 settings by default, with advanced
options hidden behind a toggle. No breaking changes - all config keys, defaults,
and functionality remain unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(yandex_music): Implement FLAC lossless playback with AES-256 decryption

This commit fully implements lossless FLAC playback for Yandex Music, fixing the
issue where only MP3 320kbps was available despite user having a premium subscription.

## Changes

### Quality Levels Restructure
- Replaced HIGH/LOSSLESS quality options with three-tier system matching reference implementation
- EFFICIENT (AAC ~64kbps) - Low quality, efficient bandwidth
- BALANCED (AAC ~192kbps) - Medium quality (new default)
- SUPERB (FLAC Lossless) - Highest quality
- Updated manifest.json and config entries to reflect new quality options

### API Authentication Fix
- Implemented manual HMAC-SHA256 sign calculation matching yandex-music-downloader-realflac
- Critical fix: Remove last character from base64-encoded sign ([:-1])
- Fixed HTTP 403 "not-allowed" errors from /get-file-info endpoint
- Uses DEFAULT_SIGN_KEY from yandex-music library

### FLAC Decryption Implementation
- Added _decrypt_track_url() method using AES-256 CTR mode
- Uses PyCrypto (pycryptodome) which supports 12-byte nonce for CTR mode
- Key is HEX-encoded (bytes.fromhex), not base64 as initially attempted
- Downloads encrypted stream, decrypts on-the-fly, saves to temp file
- Returns StreamDetails with StreamType.LOCAL_FILE pointing to decrypted temp file

### Streaming Logic Updates
- Enhanced get_stream_details() to handle encrypted URLs from encraw transport
- Detects needs_decryption flag in API response
- Falls back gracefully to MP3 if decryption fails
- Supports both encrypted and unencrypted FLAC URLs
- Updated _select_best_quality() to intelligently select based on three quality tiers

### Dependencies
- Added pycryptodome==3.21.0 to support AES CTR mode with 12-byte nonce
- Uses aiohttp for direct HTTP download of encrypted streams

### Testing
- All existing tests pass (7/7 in test_streaming.py)
- Type checking passes (mypy success)
- Code quality checks pass (ruff linter/formatter)

## Technical Details

The Yandex Music API returns encrypted URLs when using transports=encraw. The decryption
process matches the working reference implementation:

1. Calculate HMAC-SHA256 sign with all param values joined
2. Base64 encode and remove last character (critical!)
3. Request /get-file-info with quality=lossless, codecs=flac-mp4,flac,...
4. Download encrypted stream from returned URL
5. Decrypt using AES-256 CTR with 12-byte null nonce and HEX-decoded key
6. Save decrypted FLAC to temporary file
7. Return stream details pointing to temp file

## Tested

- Server starts successfully with Yandex Music provider loaded
- FLAC codec detected from API (codec=flac-mp4)
- Encryption detected and decryption executes (55MB encrypted → decrypted)
- StreamType.LOCAL_FILE allows playback from temp file
- Graceful fallback to MP3 if decryption fails

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(yandex_music): Implement on-the-fly streaming decryption for FLAC

Replace temp file approach with memory-based streaming decryption for better
performance and reduced disk I/O.

## Changes

### StreamType.CUSTOM Implementation
- Use AsyncGenerator[bytes, None] for streaming decryption
- No temporary files on disk - all processing in memory
- Store encrypted URL and key in StreamDetails.data

### Streaming Decryption Pipeline
- Download encrypted stream in 64KB chunks using aiohttp
- Decrypt incrementally with AES-256 CTR mode
- Counter auto-increments for each block (streaming-friendly)
- Yield decrypted chunks directly to audio pipeline

### Performance Improvements
- **First chunk:** 0.39s vs 5+ seconds with temp file approach
- **No disk I/O:** Streaming directly from memory
- **Lower latency:** Start playback while downloading/decrypting
- **Efficient:** 64KB chunks balance memory and throughput

### Implementation Details
- Added get_audio_stream() method to YandexMusicStreamingManager
- Cipher initialization with 12-byte null nonce (PyCrypto)
- ClientTimeout: connect=30s, sock_read=600s for stable streaming
- Proper error handling and logging throughout pipeline

### Technical Notes
AES CTR mode is ideal for streaming because:
- Each block can be encrypted/decrypted independently
- Counter increments automatically - no state management needed
- Supports arbitrary chunk sizes (not just 16-byte blocks)

## Tested
- All 7 unit tests pass
- Type checking passes (mypy)
- Code quality checks pass (ruff)
- Live streaming confirmed: codec=flac, streamtype=custom
- First audio chunk in <0.4s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(deps): Update pycryptodome to 3.23.0 to resolve dependency conflict

pywidevine==1.9.0 requires pycryptodome>=3.23.0
Updated from 3.21.0 to 3.23.0 to satisfy both dependencies

* feat(yandex_music): add High quality level (MP3 320kbps)

Add new "High" quality level between Balanced and Superb:
- Efficient (AAC ~64kbps) - Low quality
- Balanced (AAC ~192kbps) - Medium quality
- High (MP3 ~320kbps) - High quality lossy (NEW)
- Superb (FLAC Lossless) - Highest quality

Implementation:
- Add QUALITY_HIGH constant in constants.py
- Add "High (MP3 ~320kbps)" option in config UI
- Update _select_best_quality() logic to prefer MP3 >=256kbps
- Fallback chain: high bitrate MP3 → any MP3 → highest non-FLAC

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(yandex_music): add configurable FLAC streaming modes

On low-power devices, encrypted FLAC streaming breaks after ~1 minute
because AES decryption can't keep up with download speed, causing the
server to drop the connection (ClientPayloadError).

Add 3 configurable streaming modes to decouple download from decryption:
- Direct: on-the-fly decrypt (original behavior, fast devices)
- Buffered: async queue with backpressure (recommended, default)
- Preload: full download then decrypt via SpooledTemporaryFile (slow devices)

Closes #29

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(yandex_music): auto-reconnect API client after network outage

_ensure_connected() now attempts reconnect when _client is None instead
of raising immediately. Added _call_with_retry() helper that wraps API
calls with one reconnect attempt on connection errors. Refactored all
API methods to use it, eliminating ad-hoc retry loops.

Fixes permanent provider death after temporary network outage where
_reconnect() failure set _client=None with no recovery path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(yandex_music): extend recommendations with feed, chart, releases, and playlists

Add 4 new recommendation sections beyond My Wave: Made for You (personalized
feed playlists), Chart (top tracks), New Releases (albums), and New Playlists
(editorial). Each section has its own config toggle under a "Discovery" category
and independent cache TTLs (30min for feed, 1h for chart/releases/playlists).

Closes #34

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(yandex_music): enable seek in Preload streaming mode for FLAC

- Preload mode now downloads and decrypts to temp file before playback
- Returns StreamType.LOCAL_FILE with can_seek=True for proper navigation
- Files exceeding size limit (config) fall back to Buffered mode
- Rename config 'Preload memory limit' to 'Preload max file size' with updated description
- Add temp file cleanup on stream completion and provider unload

Fixes: seek not working when using Superb quality + Preload mode
Fixes: progress bar starting before audio is ready

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* feat(yandex_music): add Picks & Mixes browse and discovery sections

Browse:
- picks/ folder with mood, activity, era, genres subfolders
- mixes/ folder with seasonal collections (winter, summer, autumn, newyear)
- Each tag returns curated playlists from Yandex Music tags API

Discovery (home page):
- Top Picks: curated playlists (tag: top)
- Mood Mix: rotating mood playlists (chill, sad, romantic, party, relax)
- Activity Mix: rotating activity playlists (workout, focus, morning, evening, driving)
- Seasonal Mix: playlists based on current season

Configuration (new picks_mixes category):
- Enable Picks in Browse
- Enable Mixes in Browse
- Enable Top Picks on Home
- Enable Mood/Activity/Seasonal Mix on Home

Localization:
- All folder and tag names in Russian and English

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* feat(yandex_music): add lyrics support

- Add ProviderFeature.LYRICS to supported features
- Add get_track_lyrics() method to api_client.py
- Extend parse_track() to accept lyrics and lyrics_synced parameters
- Fetch lyrics in get_track() and attach to track metadata
- Support both synced LRC format and plain text lyrics
- Handle geo-restrictions and unavailable lyrics gracefully

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix(yandex_music): fix locale-dependent playlist names caching issue

- Split get_playlist() to separate virtual playlists (not cached) from real ones
- Virtual playlists (My Wave, Liked Tracks) now always use current locale
- Real playlists continue to be cached for 30 days
- Add debug logging for locale detection

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix(yandex_music): fix Browse Picks path construction

- Fix base path calculation in _browse_picks() and _browse_mixes()
- Previously: base was incorrectly trimming to parent path
- Now: base correctly appends to current path
- Add debug logging for troubleshooting

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix(yandex_music): add missing get_track_lyrics mock to integration tests

The lyrics feature added in 4fc11fd introduced a get_track_lyrics call
in get_track, but the test fixtures were not updated with the mock,
causing ValueError on tuple unpacking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address review comments: simplify Yandex Music provider configuration (#41)

* Initial plan

* Fix manifest.json URL and move pycryptodome to provider requirements

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Drastically reduce config entries and fix logging format

- Keep only 8 essential config entries: token, clear auth, quality, streaming mode, preload buffer, My Wave max tracks, Liked Tracks max tracks, and base URL
- Remove 20+ config entries and hardcode sane defaults
- Remove all category attributes from config entries
- Remove conditional recommendation check in setup()
- Replace all f-string logging with %s formatting (17 instances)
- Update constants.py to keep only necessary constants

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Address review feedback: fix parameter naming, temp file leak, and double close (#42)

* Initial plan

* Fix critical review issues: rename key_base64, fix double close, add temp file cleanup

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Fix linter issues: use cleanup_temp_file method and add noqa comment

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* [WIP] Fix file descriptor leak in Yandex Music provider (#44)

* Initial plan

* Fix streaming issues: FD leak, buffered fallback, size limits, task cleanup, stale files

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Fix provider issues: change INFO to DEBUG logs, fix track_map by ID

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Rename misnamed test to reflect QUALITY_BALANCED behavior

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Fix linting issues: move import to top, use Path.stat(), fix line lengths

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Fix Yandex Music AAC codec mapping and timezone awareness (#45)

* Initial plan

* Fix Yandex Music provider: AAC codec mapping, caching issues, and datetime timezone

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Restore @use_cache decorators for mood and activity mix recommendations to reduce API load

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Add test coverage for Yandex Music recommendation methods (#46)

* Initial plan

* Add comprehensive test coverage for Yandex Music recommendations

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Fix linting issues in test_recommendations.py

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Fix mypy type annotations in test_recommendations.py

Add return type annotations to async helper functions to resolve mypy errors:
- return_folder() -> RecommendationFolder (lines 783, 810)
- return_none() -> None (lines 813, 837)

Fixes CI mypy check failure.

* Initial plan

* Restore full test_recommendations.py file and add mypy return type annotations

Co-authored-by: trudenboy0 <261913410+trudenboy0@users.noreply.github.com>

* Increase Discovery initial tracks from 5 to 20

Show more tracks in the Discovery section on Home for better content visibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix Yandex Music browse: "Invalid subpath" error and empty tag folders

When pressing Play on a Picks tag folder (e.g. "chill"), the BrowseFolder
URI reconstruction loses the full path (picks/mood/chill → just chill),
causing browse() to fall through to the base class which raises
"Invalid subpath". Add tag recognition fallback before the base class call.

Empty tag folders were caused by hardcoded tag slugs not matching actual
Yandex API slugs. Add dynamic tag discovery via the landing("mixes") API
which returns real MixLink entities with valid tag URLs. Results are cached
for 1 hour with hardcoded tags as fallback.

Fixes #34

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address PR review feedback: dead code, infinite loop, lyrics optimization

- Remove unreachable _stream_preload() from get_audio_stream() (preload
  handled in get_stream_details via LOCAL_FILE)
- Add aac-mp4/he-aac-mp4 codec variants to Efficient and Balanced
  quality selection for consistency with _get_content_type()
- Switch _get_content_length() to shared http_session; add comments
  explaining why streaming methods use dedicated sessions
- Fix My Wave infinite re-fetch when all batch tracks are duplicates
  by breaking on first_track_id_this_batch is None
- Simplify _get_discovered_tags() return type from dict to list
- Add get_track_lyrics_from_track() to eliminate duplicate API call
  when track object is already available
- Update test mocks for new lyrics method

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix Picks & Mixes: use dynamic tag discovery, filter empty/useless tags

Replace hardcoded tag lists in browse with dynamically discovered tags
from Landing API. Only tags actually returned by the API are shown,
eliminating empty folders. Blacklist non-musical tags like
"albomy-s-videoshotami". Set is_playable=False on tag/category folders
to prevent loading thousands of tracks on Play. Reduce tag playlist
cache TTL from 1h to 10min for faster recovery.

Fixes #34

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Validate tags via API before showing in browse and recommendations

Replace blacklist-based filtering with runtime tag validation via
client.tags() to ensure only tags with actual playlists are displayed.
Adds new tags (top, newbies, in the mood, background) and filters out
editorial /post/ URLs from landing discovery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix exception handling, docstring, and log noise in Yandex Music provider (#52)

* Initial plan

* Fix 3 Copilot review issues in Yandex Music provider

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Fix race conditions, dead code, caching bugs, and performance issues in Yandex Music provider

Critical fixes:
- Add asyncio.Lock to protect My Wave shared mutable state from concurrent access
- Fix cache key collision in get_track by normalizing composite IDs before caching
- Fix random.choice + @use_cache interaction: tag selection now happens outside cached methods
- Lower "No liked tracks found" from warning to debug (normal for new accounts)

Dead code cleanup:
- Remove unused _decrypt_track_url from api_client.py (and unused aiohttp/AES imports)
- Remove dead _get_content_type stub from parsers.py, inline ContentType.UNKNOWN

Performance:
- Parallelize tag validation with asyncio.gather + Semaphore(8) instead of sequential
- Reuse shared aiohttp session for streaming instead of creating one per request
- Reduce search cache from 14 days to 3 hours
- Remove excessive per-batch/per-ID debug logging in liked tracks and library playlists

Code quality:
- Extract _parse_my_wave_track helper to eliminate 3x duplicated parsing logic
- Fix LRC detection: use regex r'\[\d{2}:\d{2}' instead of startswith('[')
- Replace hardcoded spring→autumn fallback with dynamic _validate_tag check
- Fix _validate_tag docstring (client.tags → client.get_tag_playlists)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address PR review: retry for lossless API, restore search cache, add quality tests

- Wrap get_track_file_info_lossless in _call_with_retry for automatic
  reconnection on transient API failures (extracted _build_signed_params
  and _do_request helpers)
- Revert search cache from 3h back to 14 days (search results are stable)
- Add 8 unit tests for Efficient and High quality selection branches

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix Yandex Music provider review issues: regex, HMAC signing, temp file cleanup (#54)

* Initial plan

* Fix LRC regex, HMAC sign construction, and temp file cleanup order

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Fix linting issues in tests

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Fix spelling in test comments

Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: trudenboy <139659391+trudenboy@users.noreply.github.com>

* Address PR review comments: fix dict equality check and remove duplicate import

- Use == instead of is for dict comparison (BROWSE_NAMES_RU)
- Remove duplicate AsyncGenerator import from TYPE_CHECKING block

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix MinimalProvider stub missing client/mass attributes in test

The test_temp_file_replacement_order test was failing because
MinimalProvider lacked client and mass attributes expected by
YandexMusicStreamingManager.__init__.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix My Wave over-fetching and locale-aware tag caching

Address Copilot review comments on PR #3147:
- _browse_my_wave: cap fetch loop to BROWSE_INITIAL_TRACKS on initial browse
  instead of post-loop slicing, preventing tracks from being marked as "seen"
  but never shown to the user
- _get_discovered_tags: add locale parameter to cache key so tag titles are
  re-fetched when locale changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix flac-mp4 container handling: separate MP4 container from FLAC codec

Yandex API returns codec "flac-mp4" meaning FLAC audio inside an MP4
container. Previously _get_content_type mapped this to ContentType.FLAC,
causing ffmpeg to misidentify the container format. Now correctly returns
(ContentType.MP4, ContentType.FLAC) as container/codec pair, matching
how Apple Music handles MP4+AAC. Also fixes temp file extension for
preload mode (.mp4 instead of .flac).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Improve flac-mp4 downstream: update logs, temp prefix, and comments

Follow-up to b6b11a2 (flac-mp4 container fix). Replaces hardcoded
"FLAC" in log messages with the actual codec string so logs correctly
reflect flac-mp4 vs flac. Renames TEMP_FILE_PREFIX to "yandex_audio_"
since temp files may now be .mp4. Adds clarifying comment about
flac-mp4 in _select_best_quality.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Set correct sample_rate/bit_depth for Yandex Music AudioFormat

The Yandex API does not return sample rate or bit depth, so AudioFormat
used model defaults (44100/16) even for flac-mp4 streams that are
actually 48kHz/24bit. This caused unnecessary downsampling in
_select_pcm_format. Add codec-based defaults via _get_audio_params and
_build_audio_format helpers to set the correct values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add configurable stream buffer for buffered streaming mode

Increase default buffer from 2MB (32 chunks) to 8MB (128 chunks)
to reduce stuttering on slow/unstable connections (~45s of FLAC audio).
Add CONF_STREAM_BUFFER_MB config entry (1-32 MB, default 8) so users
can tune the buffer size for their network conditions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix unresolved Copilot review comments in PR #3147

- Add implementation to test_temp_file_replacement_order() verifying
  new path is stored before old file deletion to prevent leaks
- Remove misplaced MinimalProvider stub code from test_get_audio_params_none()
- Add "spring" to TAG_MIXES and TAG_SLUG_CATEGORY to align with TAG_SEASONAL_MAP

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix stale comments flagged by Copilot review #3818153899

- Correct base64 padding note: SHA-256 (32 bytes) encodes with "=" not "=="
- Remove hard-coded line number reference from LRC regex test comment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix naming consistency for spring and liked_tracks in BROWSE_NAMES

Add missing "spring" and "liked_tracks" entries to both BROWSE_NAMES_RU
and BROWSE_NAMES_EN dicts, and use LIKED_TRACKS_PLAYLIST_ID constant
instead of hardcoded "tracks" key in get_playlist().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix concurrency issues and harden config entries per Copilot review

- Add asyncio.Lock to _get_streaming_session to prevent session leak on races
- Add threading.Lock to _temp_files to prevent concurrent modification
- Extract _replace_temp_file helper for atomic temp file replacement
- Clarify AES CTR cipher safety in _prepare_cipher docstring
- Clarify HMAC timestamp regeneration in _build_signed_params docstring
- Add range constraints to My Wave and Liked Tracks max tracks config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix temp_fd double-close bug and clarify seasonal fallback comment

- Move temp_fd = -1 before os.fdopen() to prevent double-close when an
  exception occurs inside the with block (Copilot #2823816163)
- Clarify constants.py comment to reflect that fallback is dynamic and
  applies to any unavailable seasonal tag (Copilot #2823816236)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix FLAC seek in buffered/direct mode and improve My Wave reliability

- Enable ffmpeg seek (allow_seek=True) for encrypted CUSTOM streams so
  that seeking fast-forwards through the decrypted pipe instead of
  restarting from the beginning; can_seek stays False since the provider
  can't reposition the AES-CTR cipher mid-stream
- Batch multiple Rotor API calls per playlist-page request to reduce
  round-trips and ensure enough tracks are returned per pagination page
- Guard radioStarted feedback so the flag is only set on confirmed send
- Promote rotor feedback errors from debug to warning for visibility

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Increase max stream buffer size to 64 MB for buffered FLAC streaming

Raises the upper bound of the stream buffer config from 32 MB to 64 MB
to accommodate longer tracks on slow/weak hardware without dropping the
connection mid-stream.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Address Copilot review comments (round 3)

- Clarify stream buffer description to mention Preload fallback scenario
- Improve HMAC sign comment: state concatenation order and reference
  yandex-music-downloader-realflac as the source of the comma-strip rule
- Add inline comments to LRC regex tests documenting \d{2,3} lower/upper bounds

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Address Copilot review comments: unquote tag slugs, skip known folders, show buffer for preload

- URL-decode tag slugs from get_landing_tags() via urllib.parse.unquote
- Skip _get_discovered_tag_slugs() API call for known top-level browse folders
- Show CONF_STREAM_BUFFER_MB config for both Buffered and Preload modes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix issue #57 (owner name locale) and issue #56 (stream retry + buffered seek)

- Fix #57: normalize all known system owner name variants to locale-aware
  canonical form ("Yandex Music" / "Яндекс Музыка") so playlist filtering
  works regardless of API locale
- Fix #56 retry: resume encrypted streams on ClientPayloadError via HTTP
  Range + AES-CTR counter resume in both direct and buffered modes (3 retries)
- Fix #56 seek: enable can_seek=True for flac-mp4 tracks; new mp4_seek.py
  parses moov/stts/stsc/stco to find byte offset; _stream_buffered_seek
  serves patched moov prefix + Range-resumed mdat from seek position

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add .worktrees/ to .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add backward-compat shim for advanced= vs category= in Yandex Music config

Replace 6 hardcoded advanced=True with **_ADVANCED that detects at runtime
whether models >=1.1.87 (advanced= field) or 1.1.86 (category= field) is
installed. This eliminates manual patching when backporting to stable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(yandex_music): remove preload streaming mode

Remove the Preload streaming mode which downloaded and cached entire
encrypted tracks to temp files on disk. This approach is incompatible
with a streaming service model and was flagged in upstream code review.

Direct and Buffered modes are sufficient: Direct decrypts on-the-fly
chunk-by-chunk, Buffered decouples download from consumption via a
bounded async queue.

Also removes the backward-compat _ADVANCED shim (replaced with
advanced=True directly) since upstream models >=1.1.87 supports it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(yandex_music): remove direct streaming mode, buffered only

Simplify FLAC streaming to a single buffered mode. The direct
on-the-fly mode adds complexity without meaningful benefit over
the buffered async-queue approach.

Removes CONF_STREAMING_MODE config entry entirely. The stream
buffer size (MB) remains as an Advanced setting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(yandex_music): fix FLAC seek by delegating to ffmpeg (-ss)

The _stream_buffered_seek approach was broken: it patched chunk offsets
in the moov atom but ffmpeg still tried to decode from sample 0 (time=0),
whose adjusted offsets pointed into the moov itself, producing empty output.

Fix: set can_seek=False, allow_seek=True for encrypted FLAC streams.
The provider always streams from position 0; ffmpeg uses -ss to seek.
Seeking now works correctly at the cost of streaming from the beginning,
which is acceptable and consistent with non-encrypted HTTP streams.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(yandex_music): include liked editorial playlists in library sync

Add get_liked_playlists() to api_client.py calling users_likes_playlists()
endpoint. Update get_library_playlists() to yield liked editorial playlists
alongside user-created ones, with deduplication via seen_ids. Fixes provider
filter showing only virtual playlists (closes #57).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(yandex_music): remove buffering/seeking infrastructure from streaming

Per maintainer feedback on PR #3147, remove all custom downloading,
buffering and seeking from the provider. Replace the ~430-line buffered
streaming implementation with a minimal on-the-fly AES-256-CTR decrypt
wrapper (~15 lines) that uses mass.http_session.

Changes:
- Remove _stream_buffered, _stream_buffered_seek, _fetch_and_decrypt_partial,
  _patch_stco, _prepare_cipher, _get_streaming_session, close()
- Delete mp4_seek.py (MP4 moov/stco parser, ~250 lines)
- Remove CONF_STREAM_BUFFER_MB config entry
- Use mass.http_session instead of custom aiohttp.ClientSession
- Seeking delegated to ffmpeg via can_seek=False / allow_seek=True

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.2.0 (#81)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.2.1 (#82)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.2.2 (#83)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.2.3 (#84)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.2.4 (#85)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.2.5 (#86)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.2.6 (#87)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.2.7 (#88)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.2.8 (#89)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.2.9 (#91)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.2.10 (#94)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(yandex_music): address PR review comments

- provider.py: fix docstring for _get_valid_tags_for_category — tags are
  validated via _validate_tag() → client.get_tag_playlists(), not client.tags()
- .gitignore: remove .worktrees/ (not applicable to upstream repo)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.3.0 (#95)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.3.1 (#96)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.3.2 (#97)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.3.3 (#98)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.4.1 (#99)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.4.2 (#100)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.5.1 (#101)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.5.2 (#102)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.5.3 (#103)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.5.4 (#104)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(yandex_music): sync provider from ma-provider-yandex-music v2.5.5 (#105)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: trudenboy0 <trudenboy0@gmail.com>
Co-authored-by: trudenboy0 <261913410+trudenboy0@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

1 participant