librespot icon indicating copy to clipboard operation
librespot copied to clipboard

Spotify lossless will not be supported

Open kingosticks opened this issue 3 months ago • 47 comments

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

kingosticks avatar Sep 15 '25 10:09 kingosticks

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.

roderickvd avatar Sep 18 '25 18:09 roderickvd

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

heinzgruber avatar Sep 19 '25 11:09 heinzgruber

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.

guredora403 avatar Sep 21 '25 22:09 guredora403

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?

clepdn avatar Sep 25 '25 04:09 clepdn

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.

roderickvd avatar Sep 25 '25 05:09 roderickvd

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.

kingosticks avatar Sep 25 '25 07:09 kingosticks

it’s the desktop application for windows, since that’s the only way I can stream lossless right now

clepdn avatar Sep 27 '25 03:09 clepdn

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 the IV (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 0xA7 byte 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.).

SuisChan avatar Sep 29 '25 14:09 SuisChan

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?

kingosticks avatar Sep 29 '25 14:09 kingosticks

but I don't see fileid d66a44eea7819cbce00e9c9e2abbe7f373bc60ed 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.

SuisChan avatar Sep 29 '25 14:09 SuisChan

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")

vemsom avatar Sep 29 '25 14:09 vemsom

@SuisChan track fb24dfb1ea0043f9afdd8633a3b75e1b I had deleted the cache so i don't think it's that, I see it doing all the other requests.

kingosticks avatar Sep 29 '25 15:09 kingosticks

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

SuisChan avatar Sep 29 '25 15:09 SuisChan

  • 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

SuisChan avatar Sep 29 '25 15:09 SuisChan

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...

SuisChan avatar Sep 29 '25 22:09 SuisChan

Oh okay, but I can confirm that the AUDIO_FILES response contains that file_id. Let Me by Anxious spotify:track:7L77eB8vi2vpuqniEHqdzl, right?

Yep, thanks. I see this now. I didn't realise there were different kinds of extended metadata requests.

kingosticks avatar Sep 29 '25 23:09 kingosticks

Hey, I have Spotify lossless on all my devices and would love to get it on Librespot. Is there anyway I can help?

Fletcher-Alderton avatar Oct 01 '25 06:10 Fletcher-Alderton

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...

AmilieCoding avatar Oct 01 '25 18:10 AmilieCoding

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(())
}

SuisChan avatar Oct 01 '25 18:10 SuisChan

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
        }
    ]
}

Lustyn avatar Oct 02 '25 02:10 Lustyn

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(()) }

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.

pickpocket avatar Oct 02 '25 18:10 pickpocket

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.

SuisChan avatar Oct 02 '25 18:10 SuisChan

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.

kingosticks avatar Oct 02 '25 20:10 kingosticks

Isn't that their DRM-based system?

That's exactly it

SuisChan avatar Oct 02 '25 20:10 SuisChan

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

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.

AmilieCoding avatar Oct 02 '25 23:10 AmilieCoding

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.

SuisChan avatar Oct 03 '25 13:10 SuisChan

so are we actually close to the implemention of the lossless in the librespot? 🤔

Laxii-null avatar Oct 03 '25 19:10 Laxii-null

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. 👍

AmilieCoding avatar Oct 04 '25 18:10 AmilieCoding

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

SuisChan avatar Oct 04 '25 20:10 SuisChan

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.

roderickvd avatar Oct 05 '25 06:10 roderickvd