* fix: use short token proxy URL to avoid firmware URL length limit on Xiaomi speakers
Small Xiaomi speakers (e.g. LX06) have an HTTP client URL length limit
of ~1024 bytes. The previous implementation base64-encoded the full
plugin-url (which itself contains a large base64 JSON payload) and
appended it as a query parameter, resulting in URLs of ~1500+ characters.
This caused the speaker firmware to truncate the request, returning 400
and triggering a playback failure.
Fix:
- In `music_library.py`: `_get_proxy_url` now generates a short
random token (10 chars) via `secrets.token_urlsafe(8)`, stores the
original URL in an in-memory cache `_proxy_token_cache`, and returns
a short URL like `/proxy/music?token=<token>` (~60 chars total).
- In `api/routers/file.py`: `/proxy/{type}` route now accepts an
optional `token` parameter. When present, it looks up the original
URL from the cache and proxies it. The `urlb64` mode is kept for
backward compatibility.
- Tokens are not consumed on first use, allowing both the speaker and
ffprobe to make multiple requests for the same track.
Tested on LX06 (小爱音箱Pro) with bilibili online music plugin.
* refactor: move _proxy_token_cache to music_library.py to avoid circular import
* fix: bilibili CDN aiohttp proxy + FFmpeg transcode to MP3 for LX06 compatibility
- Detect bilivideo/mcdn/hdslb domains in redirect loop and switch to _bilibili_ffmpeg_stream
- Use aiohttp to download MP4 (with Referer/Origin) and pipe to FFmpeg stdin to avoid CDN 403
- FFmpeg transcodes to MP3 (libmp3lame 128k) for LX06 firmware compatibility
- Fixes: FFmpeg direct CDN access returns 403, LX06 cannot play raw MP4/AAC stream
* refactor: use get/set functions for proxy token cache instead of direct import
---------
Co-authored-by: hejun041 <hejun041@users.noreply.github.com>
Add optional dirname parameter to single music download endpoint,
allowing downloads to be saved to a subdirectory under download_path.
This makes it consistent with /downloadplaylist which already supports dirname.
Changes:
- Add dirname field to DownloadOneMusic schema (default empty)
- Update download_one_music() to accept dirname parameter
- Update /downloadonemusic endpoint to pass dirname
Co-authored-by: pc hu <>
Add new endpoint to retrieve complete playback status from Xiaomi speaker,
including status, volume, and play_song_detail (position/duration).
This is useful when using pushUrl to play audio directly, as the actual
playback progress needs to be fetched from the speaker itself rather than
relying on xiaomusic's internal tracking.
Co-authored-by: pc hu <>