Spotify lossless will not be supported
Discussed in https://github.com/librespot-org/librespot/discussions/1578
Originally posted by michaelherger September 10, 2025 https://newsroom.spotify.com/2025-09-10/lossless-listening-arrives-on-spotify-premium-with-a-richer-more-detailed-listening-experience/
(no pressure 😂)
I'm focusing on a few other things now, but depending on how Spotify implemented this it may actually not be difficult.
Clearly removing the bitrate command line limitations won't work, but some years ago (yeah) I already took care to pave the way for FLAC support in the decoder and audio file selection. If the FLACs and its decryption keys are served over the same way as the Ogg Vorbis files, then that could be quick work... any takers?
Originally posted by @roderickvd in https://github.com/librespot-org/librespot/discussions/1578#discussioncomment-14377362
It would be nice to have real files to check if there is anything to take into account, like with ogg (header) https://github.com/librespot-org/librespot/blob/dev/playback/src/player.rs#L1050 but yeah, it should be a pretty quick work
Originally posted by @SuisChan in https://github.com/librespot-org/librespot/discussions/1578#discussioncomment-14377540
Appreciate all the offers for help, but please stick to the Spotify ToS: no sharing of files or accounts. We'll need someone with time and access to lossless capabilities to propose a PR.
Maybe this is possible when librespot can play lossless:
https://community.spotify.com/t5/Live-Ideas/Add-Bit-Perfect-Playback-WASAPI-Exclusive-Mode-on-Windows/idi-p/7125179
Hello! I think this is truly an amazing project. I'm currently using Spotify's Windows desktop client and am successfully achieving lossless playback, so I'd love to contribute. However, I've been unable to figure out how to read the encrypted communication in the control plane. My apologies for that. Once I know that, I could observe the communication flow during normal playback versus FLAC playback.
i have lossless working and enabled for my account
the devtools for my desktop client show an AAC_24 (24-bit?) file format from a GET spclient.wg.spotify.com/metadata/4/track/{TRACK_GID}?market=from_token
here's the request response with a bit of junk and uuids removed.
{
"gid": "...",
"name": "Besties",
...
"file": [
{
"file_id": "...",
"format": "OGG_VORBIS_320"
},
...
{
"file_id": "...",
"format": "AAC_24"
}
],
}
my devtools don't show me any actually useful information, like if it's fetching the actual audio data differently from the way it fetches other audio qualities. i maybe need a proxy or some mitm to get what the local backend is fetching?
For the AAC_24 part, probably it’s just the protobufs that need an update to map to FLAC_FLAC or similar.
Yes you can run a proxy to see where the desktop client is getting its files from.
When you say "desktop client" do you mean desktop client or do you mean web browser? We want to know what the former is doing, not the latter.
it’s the desktop application for windows, since that’s the only way I can stream lossless right now
Hey, just a quick update on lossless.
- The files are served the same way.
- The encryption mode is the same,
AES-128-CTR, as is theIV(72e067fbddc...). - And it looks like there are no additional headers or anything, just raw FLAC files without any modifications.
- Players play the files without a problem, unlike Ogg Vorbis files (if you include the
0xA7byte header).
Bitrate? Up to 24-bit (in my case, some files are up to 1800 kbps, if anyone's interested).
So, if there's a way to obtain keys through shannon, it would be easy to implement.
The other issue is that this won't be available to everyone, so some checks is needed to determine if the user is lossless-eligible (since this is a server-side restriction, different markets, etc.).
It became available on my account today but I couldn't find where the fileid my desktop app is using is actually coming from. I see requests to https://gew1-spclient.spotify.com/storage-resolve/v2/files/audio/interactive_prefetch/16/d66a44eea7819cbce00e9c9e2abbe7f373bc60ed and then https://audio-fa-l.spotifycdn.com/audio/d66a44eea7819cbce00e9c9e2abbe7f373bc60ed but I don't see fileid d66a44eea7819cbce00e9c9e2abbe7f373bc60ed anywhere in the response from https://spclient.wg.spotify.com/metadata/4/track/fb24dfb1ea0043f9afdd8633a3b75e1b Am I being dense?
but I don't see
fileidd66a44eea7819cbce00e9c9e2abbe7f373bc60ed anywhere in the response from
Must be from the extended-metadata response @ AUDIO_FILES, here it is, haven't seen it anywhere else
If you link the track URI, I can check it now
edit: In fact, the app might not have had to make a request just now to get the file_id, since it might already be cached in their database, it caches a lot of things, including protobuf responses.
The other issue is that this won't be available to everyone, so some checks is needed to determine if the user is lossless-eligible (since this is a server-side restriction, different markets, etc.).
Wouldn't this be part of the configuration? The player is configured for a specific bit rate already (for ex. BITRATE="320")
@SuisChan track fb24dfb1ea0043f9afdd8633a3b75e1b I had deleted the cache so i don't think it's that, I see it doing all the other requests.
I had deleted the cache so i don't think it's that
Oh okay, but I can confirm that the AUDIO_FILES response contains that file_id. Let Me by Anxious spotify:track:7L77eB8vi2vpuqniEHqdzl, right?
Wouldn't this be part of the configuration?
Someone might try to enable lossless without even knowing whether it's available, so... idk
- And it looks like there are no additional headers or anything, just raw FLAC files without any modifications.
huh! Even better, head-fa files are just a chunk of the original, they are identical down to the last bit
Not so good news, on my end the keys to lossless don't work via shannon, someone needs to check and confirm or prove me wrong...
Oh okay, but I can confirm that the
AUDIO_FILESresponse contains thatfile_id.Let MebyAnxiousspotify:track:7L77eB8vi2vpuqniEHqdzl, right?
Yep, thanks. I see this now. I didn't realise there were different kinds of extended metadata requests.
Hey, I have Spotify lossless on all my devices and would love to get it on Librespot. Is there anyway I can help?
Is there any specific documentation on this with Shannon? If so I'll toy around myself and see if this is just a you issue or a completely new hurdle. Would be appreciated for a link. ^-^
Not so good news, on my end the keys to lossless don't work via shannon, someone needs to check and confirm or prove me wrong...
Is there any specific documentation on this with Shannon? If so I'll toy around myself and see if this is just a you issue or a completely new hurdle. Would be appreciated for a link. ^-^
@AmilieCoding, nothing special, just use the function to request keys with the correct track_id and file_id. You can easily get the file_id (lossless) using dev-tools (spicetify enable-devtools, then ctrl+shift+D in the application), and you'll find everything you need in the playback window. I just used a slightly modified play example. https://github.com/librespot-org/librespot/blob/dev/examples/play.rs
If it works, you'll get a key. If not... well, either lossless won't be supported, or significant changes might be required...
example
use std::process::exit;
use librespot_core::{FileId, Session, SessionConfig, SpotifyId, authentication::Credentials};
use librespot_protocol::authentication::AuthenticationType;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let session_config = SessionConfig::default();
let credentials = Credentials {
username: Some("username".to_string()),
auth_type: AuthenticationType::AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS,
auth_data: b"stored_credentials".to_vec(),
};
println!("Connecting...");
let session = Session::new(session_config, None);
if let Err(e) = session.connect(credentials, false).await {
println!("Error connecting: {e}");
exit(1);
}
println!("Authenticated as '{}' !", session.username());
let track = SpotifyId::from_uri("spotify:track:2tdNI4jmNQkOT41LSTSA17").unwrap();
let file_id_24bit = FileId::from_raw(&hex::decode("c976f33893d9c3a868b353cc761e68874a226faf")?);
match session.audio_key().request(track, file_id_24bit).await {
Ok(key) => println!("key @ {:#02x?}", key.0),
Err(err) => println!("err @ {}", err.to_string()),
}
println!("Done");
Ok(())
}
I have yet to figure out keys, but I can confirm that the current format values (16 and 22) for FLAC and FLAC_24 respectively are correct. It seems the API we are currently using for retrieving audio files does not support FLAC but whatever spotify.playback_esperanto.proto.PlaybackService/GetFiles is using receives it.
POST spotify.playback_esperanto.proto.PlaybackService/GetFiles
{
"1": "spotify:track:0aNdtLrUvMMSKZ2EqEFYJK"
}
->
{
"2": [
{
"1": "0bd90f1f59709a0bb39aa599af0a7d7b69e23af3",
"2": "ogg/vorbis",
"3": 96000
},
{
"1": "009268940493d5776403c4c766df69a108aa43e5",
"2": "flac/flac",
"3": 1400000,
"4": 22
},
{
"1": "ee53b08f22eae71b171ed84c6af69e2ab4bc2337",
"2": "flac/flac",
"3": 700000,
"4": 16
},
{
"1": "73727fd0675d9bf71166ee3eecbfce3ce1265e37",
"2": "ogg/vorbis",
"3": 320000,
"4": 2
},
{
"1": "9b5d26dba99da845821d5d9da133b2ecc9f8afdc",
"2": "spac/aac",
"3": 24000,
"4": 8
},
{
"1": "a96e546c001db26943bd352e460b9affa532fe81",
"2": "ogg/vorbis",
"3": 160000,
"4": 1
}
]
}
Is there any specific documentation on this with Shannon? If so I'll toy around myself and see if this is just a you issue or a completely new hurdle. Would be appreciated for a link. ^-^
@AmilieCoding, nothing special, just use the function to request keys with the correct
track_idandfile_id. You can easily get thefile_id(lossless) using dev-tools (spicetify enable-devtools, thenctrl+shift+Din the application), and you'll find everything you need in theplaybackwindow. I just used a slightly modified play example. https://github.com/librespot-org/librespot/blob/dev/examples/play.rsIf it works, you'll get a key. If not... well, either lossless won't be supported, or significant changes might be required... example
use std::process::exit;
use librespot_core::{FileId, Session, SessionConfig, SpotifyId, authentication::Credentials}; use librespot_protocol::authentication::AuthenticationType;
#[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let session_config = SessionConfig::default();
let credentials = Credentials { username: Some("username".to_string()), auth_type: AuthenticationType::AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS, auth_data: b"stored_credentials".to_vec(), };
println!("Connecting..."); let session = Session::new(session_config, None); if let Err(e) = session.connect(credentials, false).await { println!("Error connecting: {e}"); exit(1); }
println!("Authenticated as '{}' !", session.username());
let track = SpotifyId::from_uri("spotify:track:2tdNI4jmNQkOT41LSTSA17").unwrap();
let file_id_24bit = FileId::from_raw(&hex::decode("c976f33893d9c3a868b353cc761e68874a226faf")?);
match session.audio_key().request(track, file_id_24bit).await { Ok(key) => println!("key @ {:#02x?}", key.0), Err(err) => println!("err @ {}", err.to_string()), }
println!("Done");
Ok(()) }
My account has lossless. I tested this and got err @ Service unavailable { audio key error }
I have a strong feeling this is because the session config needs to be updated.
the session config needs to be updated
Or maybe spotify intentionally locked lossless behind playplay drm. I'm monitoring several repos, and based on the issues I see, there are some changes recently. For example, now when using a free account with the old protocol (shannon), you can't request keys in most cases. It seems this now only works with a premium sub.
I did see the desktop app doing requests to playplay for keys. Isn't that their DRM-based system? But it seemed to be doing that for both lossless and lossy audio quality modes.
Isn't that their DRM-based system?
That's exactly it
Is there any specific documentation on this with Shannon? If so I'll toy around myself and see if this is just a you issue or a completely new hurdle. Would be appreciated for a link. ^-^
@AmilieCoding, nothing special, just use the function to request keys with the correct
track_idandfile_id. You can easily get thefile_id(lossless) using dev-tools (spicetify enable-devtools, thenctrl+shift+Din the application), and you'll find everything you need in theplaybackwindow. I just used a slightly modified play example. https://github.com/librespot-org/librespot/blob/dev/examples/play.rsIf it works, you'll get a key. If not... well, either lossless won't be supported, or significant changes might be required... example
I haven't been able to personally get this working - but that's just because I'm a god awful programmer.
I did manage to take a look though and I did see something about DRM keys as mentioned above by @kingosticks . Would be worth taking a look into.
Would be worth taking a look into.
Uhhh, I'm not entirely sure about that.
Spotify doesn't even want to think about anyone else using playplay DRM. I'd say that as soon as something is revealed, it will be removed due to DMCA. They even added it to their bug bounty scope list.
It's an interesting opportunity tbh, but they didn't provide enough information or criteria to be considered an acceptable bug report.
so are we actually close to the implemention of the lossless in the librespot? 🤔
Would be worth taking a look into.
Uhhh, I'm not entirely sure about that.
Spotify doesn't even want to think about anyone else using playplay DRM. I'd say that as soon as something is revealed, it will be removed due to DMCA. They even added it to their bug bounty scope list.
It's an interesting opportunity tbh, but they didn't provide enough information or criteria to be considered an acceptable bug report.
I see okay, so DRM is something to avoid as a whole from what I'm seeing? Got it. 👍
I see okay, so DRM is something to avoid as a whole from what I'm seeing? Got it. 👍
Just saying don't be surprised if all your efforts go to waste, which is what will most likely happen
I think most efforts to crack the new decryption have been from decompiling or some other white box approach. The best chance we have is by taking a black box approach: develop a decryption algorithm ourselves without looking at any protected code.