[GH-ISSUE #650] Update protobuf definitions and endpoints to use current official client ones. #380

Closed
opened 2026-02-27 19:30:18 +03:00 by kerem · 13 comments
Owner

Originally created by @sashahilton00 on GitHub (Feb 24, 2021).
Original GitHub issue: https://github.com/librespot-org/librespot/issues/650

Over the course of the past few years, Spotify has updated it's protobufs several times, along with its endpoints. This was to be expected as they continued to add functionality, nonetheless librespot is now pretty significantly diverged from the protobuf definitions that are in use in the official clients, and possibly at higher risk of experiencing issues with endpoints being depreciated at short notice, such as the recent issues Facebook login flow (admittedly not due to protobuf definition issues though).

Furthermore, newer functionality such as canvas, context management, streaming groups, etc. is implemented in the newer proto3 definitions, attached below.

Given that Spotify is slowly standardising to HTTP/Websockets/GRPC transports, and moving away from Hermes, it may be good for this project in the long run if we were to try to follow suit. To this end, further reverse engineering of the current endpoints that are in use will need to be carried out, and the existing code updated where necessary. As a side effect of this, my gut tells me that a number of the smaller issues/feature requests (eg. repeat functionality only partially working, more 'spotify-like' normalization, etc.) will be easier to implement once we are using their up to date APIs.

I will start investigating when I get some free time, and add to this comment accordingly with any findings that I turn up. Anyone else is also invited to crack out spotify-dissect and start digging through the network traffic. If the log of notes starts to become unwieldy we can always form some sort of to-do list or stick the information in a wiki page temporarily.

The latest protobufs: spotify_protobufs.zip

Originally created by @sashahilton00 on GitHub (Feb 24, 2021). Original GitHub issue: https://github.com/librespot-org/librespot/issues/650 Over the course of the past few years, Spotify has updated it's protobufs several times, along with its endpoints. This was to be expected as they continued to add functionality, nonetheless librespot is now pretty significantly diverged from the protobuf definitions that are in use in the official clients, and possibly at higher risk of experiencing issues with endpoints being depreciated at short notice, such as the recent issues Facebook login flow (admittedly not due to protobuf definition issues though). Furthermore, newer functionality such as canvas, context management, streaming groups, etc. is implemented in the newer `proto3` definitions, attached below. Given that Spotify is slowly standardising to HTTP/Websockets/GRPC transports, and moving away from Hermes, it may be good for this project in the long run if we were to try to follow suit. To this end, further reverse engineering of the current endpoints that are in use will need to be carried out, and the existing code updated where necessary. As a side effect of this, my gut tells me that a number of the smaller issues/feature requests (eg. repeat functionality only partially working, more 'spotify-like' normalization, etc.) will be easier to implement once we are using their up to date APIs. I will start investigating when I get some free time, and add to this comment accordingly with any findings that I turn up. Anyone else is also invited to crack out `spotify-dissect` and start digging through the network traffic. If the log of notes starts to become unwieldy we can always form some sort of to-do list or stick the information in a wiki page temporarily. The latest protobufs: [spotify_protobufs.zip](https://github.com/librespot-org/librespot/files/6033341/spotify_protobufs.zip)
Author
Owner

@Johannesd3 commented on GitHub (Feb 24, 2021):

One note: The best gRPC library in Rust is probably tonic (from the tokio stack). It uses prost instead of protobuf, so it might be better to switch.

<!-- gh-comment-id:784828430 --> @Johannesd3 commented on GitHub (Feb 24, 2021): One note: The best gRPC library in Rust is probably [tonic](https://github.com/hyperium/tonic) (from the tokio stack). It uses [prost](https://github.com/danburkert/prost) instead of protobuf, so it might be better to switch.
Author
Owner

@xou816 commented on GitHub (Mar 19, 2021):

Never done that so I can't promise I can help, but I might give it a try, it sounds interesting! Is there a place where you (and others) will be uploading your findings?

<!-- gh-comment-id:803088811 --> @xou816 commented on GitHub (Mar 19, 2021): Never done that so I can't promise I can help, but I might give it a try, it sounds interesting! Is there a place where you (and others) will be uploading your findings?
Author
Owner

@sashahilton00 commented on GitHub (May 8, 2021):

Ok, so what I'm going to do is dedicate a comment to each endpoint that I reverse. I'll attach screenshots of the decoded protobufs (request/response) where it may be helpful, and reference the endpoint, along with the proto definitions.

The proto files I'm using I extracted earlier, available here.

The proto descriptor is useful for anyone looking to do their own poking around, uploaded here. Alternatively, you can compile if from the proto definitions above using a variant of the command:

protoc --descriptor_set_out=descriptor.desc **/*.proto *.proto

<!-- gh-comment-id:835579711 --> @sashahilton00 commented on GitHub (May 8, 2021): Ok, so what I'm going to do is dedicate a comment to each endpoint that I reverse. I'll attach screenshots of the decoded protobufs (request/response) where it may be helpful, and reference the endpoint, along with the proto definitions. The proto files I'm using I extracted earlier, available [here](https://github.com/librespot-org/librespot/files/6446732/spotify_protos.zip). The proto descriptor is useful for anyone looking to do their own poking around, uploaded [here](https://github.com/librespot-org/librespot/files/6446735/spotify_proto_descriptor.zip). Alternatively, you can compile if from the proto definitions above using a variant of the command: `protoc --descriptor_set_out=descriptor.desc **/*.proto *.proto`
Author
Owner

@sashahilton00 commented on GitHub (May 8, 2021):

Extended Metadata Endpoint (Protobuf)

Endpoint: https://spclient.wg.spotify.com/extended-metadata/v0/extended-metadata

Proto definitions (refer to extended_metadata.proto):

  • Request: spotify.extendedmetadata.BatchedEntityRequest. It may also accept spotify.extendedmetadata.EntityRequest, but untested.
  • Response: spotify.extendedmetadata.BatchedExtensionResponse.

Decoded Request Screenshot

cap_extendedmetadata_request

Decoded Response Screenshot

cap_extendedmetadata_response

<!-- gh-comment-id:835581496 --> @sashahilton00 commented on GitHub (May 8, 2021): # Extended Metadata Endpoint (Protobuf) Endpoint: `https://spclient.wg.spotify.com/extended-metadata/v0/extended-metadata` Proto definitions (refer to `extended_metadata.proto`): - Request: `spotify.extendedmetadata.BatchedEntityRequest`. It may also accept `spotify.extendedmetadata.EntityRequest`, but untested. - Response: `spotify.extendedmetadata.BatchedExtensionResponse`. ## Decoded Request Screenshot ![cap_extendedmetadata_request](https://user-images.githubusercontent.com/4185362/117556491-723f4f00-b061-11eb-9ba8-ebd9aee8d680.PNG) ## Decoded Response Screenshot ![cap_extendedmetadata_response](https://user-images.githubusercontent.com/4185362/117556537-eed22d80-b061-11eb-8597-0a62164848d8.PNG)
Author
Owner

@sashahilton00 commented on GitHub (May 9, 2021):

User Customisation Endpoint (Protobuf)

Endpoint: https://spclient.wg.spotify.com/extended-metadata/v0/extended-metadata

Proto definitions (refer to ucs.proto (user customisation service)):

  • Request: spotify.remote_config.ucs.proto.UcsRequest.
  • Response: spotify.remote_config.ucs.proto.UcsResponseWrapper.

Notes

I didn't think much of ths endpoint initially, it seemed to just be a bunch of A/B test flags, but on closer inspection, it appears to carry a bunch of possibly useful information. Besides feature flags (one of which is Spotify HiFi), it has buffer parameters, prefetch configuration, account status and permissions, api base urls, etc. Instead of a screenshot, I've posted the request/response json below (converted from protobuf)
@roderickvd loudness-levels might be interesting for you. There's also some other related stuff.

Request Payload
caller_info {
  request_origin_id: "desktop"
  request_orgin_version: "1.1.58.820"
  reason: "BACKGROUND_SYNC"
}
resolve_request {
  property_set_id: "762...cd5"
  fetch_type {
    type: BACKGROUND_SYNC
  }
  context {
    context {
      known_context: KNOWN_CONTEXT_INSTALLATION_ID
      value: "188...706"
    }
    context {
      known_context: KNOWN_CONTEXT_VERSION
      value: "1.1.58.820"
    }
  }
}
account_attributes_request {
}
Response Payload
success {
  resolve_success {
    configuration {
      configuration_assignment_id: "3f3...da3"
      fetch_time_millis: 1620515556953
      assigned_values {
        property_id {
          scope: "core-metadata-feature"
          name: "metadata_over_exts_rollout"
        }
        metadata {
          policy_id: 12959
          external_realm: "exp-planner"
          external_realm_id: 312886
        }
        enum_value {
          value: "EnabledWithMercury"
        }
      }
      assigned_values {
        property_id {
          scope: "core-player"
          name: "injection_enabled"
        }
        metadata {
          policy_id: 7669
          external_realm: "exp-planner"
          external_realm_id: 29993
        }
        enum_value {
          value: "Enabled"
        }
      }
      assigned_values {
        property_id {
          scope: "core-collection-feature"
          name: "injection_offline_enabled"
        }
        metadata {
          policy_id: 7669
          external_realm: "exp-planner"
          external_realm_id: 29993
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-playlist-feature"
          name: "injection_offline_enabled"
        }
        metadata {
          policy_id: 7669
          external_realm: "exp-planner"
          external_realm_id: 29993
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-connect-feature"
          name: "connect_dial_wol_enabled"
        }
        metadata {
          policy_id: 8302
          external_realm: "exp-planner"
          external_realm_id: 31258
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-connect-feature"
          name: "connect_use_injected_http_dial"
        }
        metadata {
          policy_id: 8484
          external_realm: "exp-planner"
          external_realm_id: 31953
        }
        bool_value {
        }
      }
      assigned_values {
        property_id {
          scope: "core-player-feature"
          name: "context_resolve_connectivity"
        }
        metadata {
          policy_id: 8596
          external_realm: "exp-planner"
          external_realm_id: 32404
        }
        enum_value {
          value: "WebGate"
        }
      }
      assigned_values {
        property_id {
          scope: "core-connect-feature"
          name: "show_offline_devices_in_core"
        }
        metadata {
          policy_id: 9128
          external_realm: "exp-planner"
          external_realm_id: 33532
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-connect-feature"
          name: "connect_ios_mdns_discovery"
        }
        metadata {
          policy_id: 9591
          external_realm: "exp-planner"
          external_realm_id: 9521
        }
        enum_value {
          value: "Combined"
        }
      }
      assigned_values {
        property_id {
          scope: "core-connect-feature"
          name: "connect_device_capabilities_required"
        }
        metadata {
          policy_id: 9595
          external_realm: "exp-planner"
          external_realm_id: 12678
        }
        bool_value {
        }
      }
      assigned_values {
        property_id {
          scope: "core-connect-feature"
          name: "allow_mft_to_mft_transfers"
        }
        metadata {
          policy_id: 9596
          external_realm: "exp-planner"
          external_realm_id: 12680
        }
        bool_value {
        }
      }
      assigned_values {
        property_id {
          scope: "core-connect-feature"
          name: "transfer_blob_enabled"
        }
        metadata {
          policy_id: 9774
          external_realm: "exp-planner"
          external_realm_id: 11016
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "client-desktop-shell"
          name: "enable_cpu_load_monitoring"
        }
        metadata {
          policy_id: 9838
          external_realm: "exp-planner"
          external_realm_id: 13159
        }
        bool_value {
        }
      }
      assigned_values {
        property_id {
          scope: "client-desktop-shell"
          name: "enable_device_info_logging_videocapture"
        }
        metadata {
          policy_id: 9839
          external_realm: "exp-planner"
          external_realm_id: 13161
        }
        bool_value {
        }
      }
      assigned_values {
        property_id {
          scope: "client-desktop-shell"
          name: "enable_device_info_logging"
        }
        metadata {
          policy_id: 9840
          external_realm: "exp-planner"
          external_realm_id: 13163
        }
        bool_value {
        }
      }
      assigned_values {
        property_id {
          scope: "core-player-feature"
          name: "affinity_connectivity"
        }
        metadata {
          policy_id: 10016
          external_realm: "exp-planner"
          external_realm_id: 34622
        }
        enum_value {
          value: "WebGate"
        }
      }
      assigned_values {
        property_id {
          scope: "core-player"
          name: "enable_kittehbox"
        }
        metadata {
          policy_id: 10154
          external_realm: "exp-planner"
          external_realm_id: 13532
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-perf-metrics-feature"
          name: "should_flush_request_accounting_on_cold_startup"
        }
        metadata {
          policy_id: 10424
          external_realm: "exp-planner"
          external_realm_id: 9628
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "client-desktop-client-updates"
          name: "enable_updates_over_webgate"
        }
        metadata {
          policy_id: 10459
          external_realm: "exp-planner"
          external_realm_id: 10143
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "client-desktop-cast"
          name: "enable_cast_discovery"
        }
        metadata {
          policy_id: 10579
          external_realm: "exp-planner"
          external_realm_id: 11967
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "client-desktop-shell"
          name: "show_offline_menu_option"
        }
        metadata {
          policy_id: 10590
          external_realm: "exp-planner"
          external_realm_id: 12287
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-player"
          name: "enable_vodcast_video"
        }
        metadata {
          policy_id: 10629
          external_realm: "exp-planner"
          external_realm_id: 12728
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "client-desktop-client-updates"
          name: "enable_win32_background_updates"
        }
        metadata {
          policy_id: 10643
          external_realm: "exp-planner"
          external_realm_id: 12919
        }
        bool_value {
        }
      }
      assigned_values {
        property_id {
          scope: "client-desktop-memusage"
          name: "enable_mem_load_monitoring"
        }
        metadata {
          policy_id: 10651
          external_realm: "exp-planner"
          external_realm_id: 13031
        }
        bool_value {
        }
      }
      assigned_values {
        property_id {
          scope: "core-ads"
          name: "event_sender_for_ad_event"
        }
        metadata {
          policy_id: 11081
          external_realm: "exp-planner"
          external_realm_id: 35755
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-connect-feature"
          name: "connect_strip_player_states"
        }
        metadata {
          policy_id: 11383
          external_realm: "exp-planner"
          external_realm_id: 28418
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-facebook"
          name: "facebook_http_enable"
        }
        metadata {
          policy_id: 11502
          external_realm: "exp-planner"
          external_realm_id: 329256
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-player-feature"
          name: "voice_context_connectivity"
        }
        metadata {
          policy_id: 11558
          external_realm: "exp-planner"
          external_realm_id: 329473
        }
        enum_value {
          value: "WebGate"
        }
      }
      assigned_values {
        property_id {
          scope: "client-desktop-shell"
          name: "use_event_persistence"
        }
        metadata {
          policy_id: 11750
          external_realm: "exp-planner"
          external_realm_id: 330327
        }
        bool_value {
        }
      }
      assigned_values {
        property_id {
          scope: "core-connect-feature"
          name: "set_server_time_from_connect"
        }
        metadata {
          policy_id: 12253
          external_realm: "exp-planner"
          external_realm_id: 332474
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-offline"
          name: "offline2_over_http"
        }
        metadata {
          policy_id: 12274
          external_realm: "exp-planner"
          external_realm_id: 332752
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "client-desktop-shell"
          name: "useCrossPlatformUIv2"
        }
        metadata {
          policy_id: 12365
          external_realm: "exp-planner"
          external_realm_id: 333070
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-connectivity-sdk"
          name: "product_state_write_to_client_settings"
        }
        metadata {
          policy_id: 12413
          external_realm: "exp-planner"
          external_realm_id: 333186
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-audio-track-player-feature"
          name: "enable_stereo_to_mono_downmixer"
        }
        metadata {
          policy_id: 12723
          external_realm: "exp-planner"
          external_realm_id: 333409
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-podcast-ads-feature"
          name: "enable_podcast_ad_segments_metadata"
        }
        metadata {
          policy_id: 13111
          external_realm: "exp-planner"
          external_realm_id: 311766
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-played-state"
          name: "enable_periodic_post_to_backend"
        }
        metadata {
          policy_id: 13160
          external_realm: "exp-planner"
          external_realm_id: 1000456
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-media-parsers-feature"
          name: "use_platform_media_parser_windows"
        }
        metadata {
          policy_id: 13481
          external_realm: "exp-planner"
          external_realm_id: 1001026
        }
        bool_value {
          value: true
        }
      }
      assigned_values {
        property_id {
          scope: "core-player"
          name: "enable_automix_rules"
        }
        metadata {
          policy_id: 13746
          external_realm: "exp-planner"
          external_realm_id: 1003513
        }
        bool_value {
        }
      }
      assigned_values {
        property_id {
          scope: "core-collection-feature"
          name: "core_use_collection2_for_artists"
        }
        metadata {
          policy_id: 13748
          external_realm: "exp-planner"
          external_realm_id: 1000239
        }
        bool_value {
        }
      }
      assigned_values {
        property_id {
          scope: "core-playlist-feature"
          name: "playlist_sync_events_logging_level"
        }
        metadata {
          policy_id: 14008
          external_realm: "exp-planner"
          external_realm_id: 1004493
        }
        enum_value {
          value: "Error"
        }
      }
      assigned_values {
        property_id {
          scope: "core-context-track-exts"
          name: "enable_podcast_poll"
        }
        metadata {
          policy_id: 14219
          external_realm: "exp-planner"
          external_realm_id: 1004412
        }
        bool_value {
          value: true
        }
      }
    }
  }
  account_attributes_success {
    account_attributes {
      key: "remote-control"
      value {
        string_value: "6"
      }
    }
    account_attributes {
      key: "publish-activity"
      value {
        string_value: "0"
      }
    }
    account_attributes {
      key: "audio-preview-url-template"
      value {
        string_value: "https://p.scdn.co/mp3-preview/{id}"
      }
    }
    account_attributes {
      key: "incognito_mode_timeout"
      value {
        long_value: 21600
      }
    }
    account_attributes {
      key: "ad-formats-preroll-video"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "type"
      value {
        string_value: "premium"
      }
    }
    account_attributes {
      key: "ab_recently_played_feature_time_filter_threshold"
      value {
        string_value: "com.spotify.gaia=30,driving-mode=120,spotify%3Ainternal%3Astartpage=30"
      }
    }
    account_attributes {
      key: "license-agreements"
      value {
        string_value: ""
      }
    }
    account_attributes {
      key: "publish-playlist"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "buffering-strategy"
      value {
        string_value: "0"
      }
    }
    account_attributes {
      key: "expiry"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "ab-desktop-hide-follow"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "ab-collection-hide-unavailable-albums"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "payments-initial-campaign"
      value {
        string_value: "web"
      }
    }
    account_attributes {
      key: "capper-profile"
      value {
        string_value: ""
      }
    }
    account_attributes {
      key: "profile-image-upload"
      value {
        string_value: "1"
      }
    }
    account_attributes {
      key: "enable-annotations"
      value {
        string_value: "2"
      }
    }
    account_attributes {
      key: "ab-ad-player-targeting"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "india-experience"
      value {
        string_value: "0"
      }
    }
    account_attributes {
      key: "video-wifi-initial-bitrate"
      value {
        long_value: 800000
      }
    }
    account_attributes {
      key: "ab-mobile-running-onlymanualmode"
      value {
        string_value: "only-manual"
      }
    }
    account_attributes {
      key: "playlist-annotations-markup"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "prefetch-keys"
      value {
        string_value: "1"
      }
    }
    account_attributes {
      key: "allow-override-internal-prefs"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "prefetch-window-max"
      value {
        long_value: 2
      }
    }
    account_attributes {
      key: "send-email"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "ad-formats-video-takeover"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "npt-disabled"
      value {
        string_value: "2"
      }
    }
    account_attributes {
      key: "ab-collection-bookmark-model"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "pause-after"
      value {
        long_value: 0
      }
    }
    account_attributes {
      key: "local-files-import"
      value {
        string_value: "0"
      }
    }
    account_attributes {
      key: "video-manifest-url"
      value {
        string_value: "https://spclient.wg.spotify.com/manifests/v6/{type}/sources/{source_id}/options/supports_drm"
      }
    }
    account_attributes {
      key: "playlist-folders"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "ab-sugarpills-sanity-check"
      value {
        string_value: "0"
      }
    }
    account_attributes {
      key: "arsenal_country"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "track-cap"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "ab-mobile-running-tempo-detection"
      value {
        string_value: "Control"
      }
    }
    account_attributes {
      key: "shows-collection"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "is_email_verified"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "enable-annotations-read"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "addon-hifi"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "mobile-browse"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "payments-locked-state"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "shows-collection-jam"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "ab-browse-music-tuesday"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "offline"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "streaming"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "fb-info-confirmation"
      value {
        string_value: "control"
      }
    }
    account_attributes {
      key: "audio-quality"
      value {
        string_value: "1"
      }
    }
    account_attributes {
      key: "taste-onboarding-disabled"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "wanted-licenses"
      value {
        string_value: ""
      }
    }
    account_attributes {
      key: "lastfm-session"
      value {
        string_value: "b87...039|sashahilton00"
      }
    }
    account_attributes {
      key: "ab-desktop-playlist-annotation-edit"
      value {
        long_value: 1
      }
    }
    account_attributes {
      key: "ab-mobile-discover"
      value {
        long_value: 0
      }
    }
    account_attributes {
      key: "widevine-license-url"
      value {
        string_value: "https://spclient.wg.spotify.com/widevine-license/v1/video/license"
      }
    }
    account_attributes {
      key: "key-caching-max-count"
      value {
        long_value: 10000
      }
    }
    account_attributes {
      key: "ad-session-persistence"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "video-initial-bitrate"
      value {
        string_value: "200000"
      }
    }
    account_attributes {
      key: "filter-explicit-content"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "ab-play-history"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "payment-state"
      value {
        string_value: ""
      }
    }
    account_attributes {
      key: "mobile-payment"
      value {
        string_value: "0"
      }
    }
    account_attributes {
      key: "key-caching-max-offline-seconds"
      value {
        long_value: 1800
      }
    }
    account_attributes {
      key: "ugc-abuse-report-url"
      value {
        string_value: "https://support.spotify.com/abuse/?uri={uri}"
      }
    }
    account_attributes {
      key: "shuffle-algorithm"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "use-pl3"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "image-url"
      value {
        string_value: "https://i.scdn.co/image/{file_id}"
      }
    }
    account_attributes {
      key: "use-playlist-app"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "metadata-link-lookup-modes"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "enable-autostart"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "shuffle"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "instant-search"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "instant-search-expand-sidebar"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "use-playlist-uris"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "user-profile-show-invitation-codes"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "ab-ad-requester"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "nft-disabled"
      value {
        string_value: "1"
      }
    }
    account_attributes {
      key: "payments-cancel-state-interstitial"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "ab-collection-union"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "libspotify"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "key-memory-cache-mode"
      value {
        string_value: "1:15,300"
      }
    }
    account_attributes {
      key: "high-bitrate"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "head-file-caching"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "radio"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "key-caching-auto-offline"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "explicit-content"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "video-cdn-sampling"
      value {
        long_value: 1
      }
    }
    account_attributes {
      key: "ab-android-push-notifications"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "unrestricted"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "use-fb-publish-backend"
      value {
        long_value: 2
      }
    }
    account_attributes {
      key: "app-developer"
      value {
        long_value: 1
      }
    }
    account_attributes {
      key: "enable-gapless"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "buffering-strategy-parameters"
      value {
        string_value: "0.8:0.2:0.0:0.0:0.0:0.0:1.0:10:10:2000:10000:10485760"
      }
    }
    account_attributes {
      key: "ab-playlist-extender"
      value {
        long_value: 5
      }
    }
    account_attributes {
      key: "enable-crossfade"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "ad-persist-reward-time"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "public-toplist"
      value {
        string_value: "2"
      }
    }
    account_attributes {
      key: "network-operator-premium-activation"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "request-time"
      value {
        string_value: "6"
      }
    }
    account_attributes {
      key: "video-device-blacklisted"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "collection"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "fb-grant-permission-local-render"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "backend-advised-bitrate"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "catalogue"
      value {
        string_value: "premium"
      }
    }
    account_attributes {
      key: "storage-size-config"
      value {
        string_value: "10240,90,500,3"
      }
    }
    account_attributes {
      key: "ads"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "head-files-url"
      value {
        string_value: "https://heads-fa.scdn.co/head/{file_id}"
      }
    }
    account_attributes {
      key: "browse-overview-enabled"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "ab-collection-followed-artists-only"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "on-demand"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "ap-resolve-pods"
      value {
        string_value: "0"
      }
    }
    account_attributes {
      key: "name"
      value {
        string_value: "Spotify Premium"
      }
    }
    account_attributes {
      key: "sidebar-navigation-enabled"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "loudness-levels"
      value {
        string_value: "1:-9.0,0.0,3.0:-2.0"
      }
    }
    account_attributes {
      key: "mobile-login"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "preferred-locale"
      value {
        string_value: "en-gb"
      }
    }
    account_attributes {
      key: "license-acceptance-grace-days"
      value {
        long_value: 30
      }
    }
    account_attributes {
      key: "prefetch-strategy"
      value {
        long_value: 2
      }
    }
    account_attributes {
      key: "ugc-abuse-report"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "ab-watch-now"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "payments-latest-reusable-provider"
      value {
        string_value: "paypal;2020-06-15"
      }
    }
    account_attributes {
      key: "financial-product"
      value {
        string_value: "pr:premium,tc:0"
      }
    }
    account_attributes {
      key: "ab-collection-offline-mode"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "streaming-rules"
      value {
        string_value: ""
      }
    }
    account_attributes {
      key: "head-files"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "capping-bar-threshold"
      value {
        long_value: 3601
      }
    }
    account_attributes {
      key: "ab-new-share-flow"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "video-keyframe-url"
      value {
        string_value: "http://keyframes-fa.cdn.spotify.com/keyframes/v1/sources/{source_id}/keyframe/heights/{height}/timestamps/{timestamp_ms}.jpg"
      }
    }
    account_attributes {
      key: "mobile"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "is_maybe_in_social_session"
      value {
        bool_value: false
      }
    }
    account_attributes {
      key: "ab-mobile-startpage"
      value {
        long_value: 0
      }
    }
    account_attributes {
      key: "ab-moments-experience"
      value {
        string_value: "0"
      }
    }
    account_attributes {
      key: "ab-mobile-social-feed"
      value {
        string_value: "1"
      }
    }
    account_attributes {
      key: "ab-test-group"
      value {
        long_value: 972
      }
    }
    account_attributes {
      key: "player-license"
      value {
        string_value: "premium"
      }
    }
    account_attributes {
      key: "country_code"
      value {
        string_value: "GB"
      }
    }
    account_attributes {
      key: "com.spotify.madprops.delivered.by.ucs"
      value {
        bool_value: true
      }
    }
    account_attributes {
      key: "com.spotify.madprops.use.ucs.product.state"
      value {
        bool_value: false
      }
    }
  }
  fetch_time_millis: 1620515556953
}
<!-- gh-comment-id:835591130 --> @sashahilton00 commented on GitHub (May 9, 2021): # User Customisation Endpoint (Protobuf) Endpoint: `https://spclient.wg.spotify.com/extended-metadata/v0/extended-metadata` Proto definitions (refer to `ucs.proto` (user customisation service)): - Request: `spotify.remote_config.ucs.proto.UcsRequest`. - Response: `spotify.remote_config.ucs.proto.UcsResponseWrapper`. ## Notes I didn't think much of ths endpoint initially, it seemed to just be a bunch of A/B test flags, but on closer inspection, it appears to carry a bunch of possibly useful information. Besides feature flags (one of which is Spotify HiFi), it has buffer parameters, prefetch configuration, account status and permissions, api base urls, etc. Instead of a screenshot, I've posted the request/response json below (converted from protobuf) @roderickvd `loudness-levels` might be interesting for you. There's also some other related stuff. <details> <summary>Request Payload</summary> ``` caller_info { request_origin_id: "desktop" request_orgin_version: "1.1.58.820" reason: "BACKGROUND_SYNC" } resolve_request { property_set_id: "762...cd5" fetch_type { type: BACKGROUND_SYNC } context { context { known_context: KNOWN_CONTEXT_INSTALLATION_ID value: "188...706" } context { known_context: KNOWN_CONTEXT_VERSION value: "1.1.58.820" } } } account_attributes_request { } ``` </details> <details> <summary>Response Payload</summary> ``` success { resolve_success { configuration { configuration_assignment_id: "3f3...da3" fetch_time_millis: 1620515556953 assigned_values { property_id { scope: "core-metadata-feature" name: "metadata_over_exts_rollout" } metadata { policy_id: 12959 external_realm: "exp-planner" external_realm_id: 312886 } enum_value { value: "EnabledWithMercury" } } assigned_values { property_id { scope: "core-player" name: "injection_enabled" } metadata { policy_id: 7669 external_realm: "exp-planner" external_realm_id: 29993 } enum_value { value: "Enabled" } } assigned_values { property_id { scope: "core-collection-feature" name: "injection_offline_enabled" } metadata { policy_id: 7669 external_realm: "exp-planner" external_realm_id: 29993 } bool_value { value: true } } assigned_values { property_id { scope: "core-playlist-feature" name: "injection_offline_enabled" } metadata { policy_id: 7669 external_realm: "exp-planner" external_realm_id: 29993 } bool_value { value: true } } assigned_values { property_id { scope: "core-connect-feature" name: "connect_dial_wol_enabled" } metadata { policy_id: 8302 external_realm: "exp-planner" external_realm_id: 31258 } bool_value { value: true } } assigned_values { property_id { scope: "core-connect-feature" name: "connect_use_injected_http_dial" } metadata { policy_id: 8484 external_realm: "exp-planner" external_realm_id: 31953 } bool_value { } } assigned_values { property_id { scope: "core-player-feature" name: "context_resolve_connectivity" } metadata { policy_id: 8596 external_realm: "exp-planner" external_realm_id: 32404 } enum_value { value: "WebGate" } } assigned_values { property_id { scope: "core-connect-feature" name: "show_offline_devices_in_core" } metadata { policy_id: 9128 external_realm: "exp-planner" external_realm_id: 33532 } bool_value { value: true } } assigned_values { property_id { scope: "core-connect-feature" name: "connect_ios_mdns_discovery" } metadata { policy_id: 9591 external_realm: "exp-planner" external_realm_id: 9521 } enum_value { value: "Combined" } } assigned_values { property_id { scope: "core-connect-feature" name: "connect_device_capabilities_required" } metadata { policy_id: 9595 external_realm: "exp-planner" external_realm_id: 12678 } bool_value { } } assigned_values { property_id { scope: "core-connect-feature" name: "allow_mft_to_mft_transfers" } metadata { policy_id: 9596 external_realm: "exp-planner" external_realm_id: 12680 } bool_value { } } assigned_values { property_id { scope: "core-connect-feature" name: "transfer_blob_enabled" } metadata { policy_id: 9774 external_realm: "exp-planner" external_realm_id: 11016 } bool_value { value: true } } assigned_values { property_id { scope: "client-desktop-shell" name: "enable_cpu_load_monitoring" } metadata { policy_id: 9838 external_realm: "exp-planner" external_realm_id: 13159 } bool_value { } } assigned_values { property_id { scope: "client-desktop-shell" name: "enable_device_info_logging_videocapture" } metadata { policy_id: 9839 external_realm: "exp-planner" external_realm_id: 13161 } bool_value { } } assigned_values { property_id { scope: "client-desktop-shell" name: "enable_device_info_logging" } metadata { policy_id: 9840 external_realm: "exp-planner" external_realm_id: 13163 } bool_value { } } assigned_values { property_id { scope: "core-player-feature" name: "affinity_connectivity" } metadata { policy_id: 10016 external_realm: "exp-planner" external_realm_id: 34622 } enum_value { value: "WebGate" } } assigned_values { property_id { scope: "core-player" name: "enable_kittehbox" } metadata { policy_id: 10154 external_realm: "exp-planner" external_realm_id: 13532 } bool_value { value: true } } assigned_values { property_id { scope: "core-perf-metrics-feature" name: "should_flush_request_accounting_on_cold_startup" } metadata { policy_id: 10424 external_realm: "exp-planner" external_realm_id: 9628 } bool_value { value: true } } assigned_values { property_id { scope: "client-desktop-client-updates" name: "enable_updates_over_webgate" } metadata { policy_id: 10459 external_realm: "exp-planner" external_realm_id: 10143 } bool_value { value: true } } assigned_values { property_id { scope: "client-desktop-cast" name: "enable_cast_discovery" } metadata { policy_id: 10579 external_realm: "exp-planner" external_realm_id: 11967 } bool_value { value: true } } assigned_values { property_id { scope: "client-desktop-shell" name: "show_offline_menu_option" } metadata { policy_id: 10590 external_realm: "exp-planner" external_realm_id: 12287 } bool_value { value: true } } assigned_values { property_id { scope: "core-player" name: "enable_vodcast_video" } metadata { policy_id: 10629 external_realm: "exp-planner" external_realm_id: 12728 } bool_value { value: true } } assigned_values { property_id { scope: "client-desktop-client-updates" name: "enable_win32_background_updates" } metadata { policy_id: 10643 external_realm: "exp-planner" external_realm_id: 12919 } bool_value { } } assigned_values { property_id { scope: "client-desktop-memusage" name: "enable_mem_load_monitoring" } metadata { policy_id: 10651 external_realm: "exp-planner" external_realm_id: 13031 } bool_value { } } assigned_values { property_id { scope: "core-ads" name: "event_sender_for_ad_event" } metadata { policy_id: 11081 external_realm: "exp-planner" external_realm_id: 35755 } bool_value { value: true } } assigned_values { property_id { scope: "core-connect-feature" name: "connect_strip_player_states" } metadata { policy_id: 11383 external_realm: "exp-planner" external_realm_id: 28418 } bool_value { value: true } } assigned_values { property_id { scope: "core-facebook" name: "facebook_http_enable" } metadata { policy_id: 11502 external_realm: "exp-planner" external_realm_id: 329256 } bool_value { value: true } } assigned_values { property_id { scope: "core-player-feature" name: "voice_context_connectivity" } metadata { policy_id: 11558 external_realm: "exp-planner" external_realm_id: 329473 } enum_value { value: "WebGate" } } assigned_values { property_id { scope: "client-desktop-shell" name: "use_event_persistence" } metadata { policy_id: 11750 external_realm: "exp-planner" external_realm_id: 330327 } bool_value { } } assigned_values { property_id { scope: "core-connect-feature" name: "set_server_time_from_connect" } metadata { policy_id: 12253 external_realm: "exp-planner" external_realm_id: 332474 } bool_value { value: true } } assigned_values { property_id { scope: "core-offline" name: "offline2_over_http" } metadata { policy_id: 12274 external_realm: "exp-planner" external_realm_id: 332752 } bool_value { value: true } } assigned_values { property_id { scope: "client-desktop-shell" name: "useCrossPlatformUIv2" } metadata { policy_id: 12365 external_realm: "exp-planner" external_realm_id: 333070 } bool_value { value: true } } assigned_values { property_id { scope: "core-connectivity-sdk" name: "product_state_write_to_client_settings" } metadata { policy_id: 12413 external_realm: "exp-planner" external_realm_id: 333186 } bool_value { value: true } } assigned_values { property_id { scope: "core-audio-track-player-feature" name: "enable_stereo_to_mono_downmixer" } metadata { policy_id: 12723 external_realm: "exp-planner" external_realm_id: 333409 } bool_value { value: true } } assigned_values { property_id { scope: "core-podcast-ads-feature" name: "enable_podcast_ad_segments_metadata" } metadata { policy_id: 13111 external_realm: "exp-planner" external_realm_id: 311766 } bool_value { value: true } } assigned_values { property_id { scope: "core-played-state" name: "enable_periodic_post_to_backend" } metadata { policy_id: 13160 external_realm: "exp-planner" external_realm_id: 1000456 } bool_value { value: true } } assigned_values { property_id { scope: "core-media-parsers-feature" name: "use_platform_media_parser_windows" } metadata { policy_id: 13481 external_realm: "exp-planner" external_realm_id: 1001026 } bool_value { value: true } } assigned_values { property_id { scope: "core-player" name: "enable_automix_rules" } metadata { policy_id: 13746 external_realm: "exp-planner" external_realm_id: 1003513 } bool_value { } } assigned_values { property_id { scope: "core-collection-feature" name: "core_use_collection2_for_artists" } metadata { policy_id: 13748 external_realm: "exp-planner" external_realm_id: 1000239 } bool_value { } } assigned_values { property_id { scope: "core-playlist-feature" name: "playlist_sync_events_logging_level" } metadata { policy_id: 14008 external_realm: "exp-planner" external_realm_id: 1004493 } enum_value { value: "Error" } } assigned_values { property_id { scope: "core-context-track-exts" name: "enable_podcast_poll" } metadata { policy_id: 14219 external_realm: "exp-planner" external_realm_id: 1004412 } bool_value { value: true } } } } account_attributes_success { account_attributes { key: "remote-control" value { string_value: "6" } } account_attributes { key: "publish-activity" value { string_value: "0" } } account_attributes { key: "audio-preview-url-template" value { string_value: "https://p.scdn.co/mp3-preview/{id}" } } account_attributes { key: "incognito_mode_timeout" value { long_value: 21600 } } account_attributes { key: "ad-formats-preroll-video" value { bool_value: false } } account_attributes { key: "type" value { string_value: "premium" } } account_attributes { key: "ab_recently_played_feature_time_filter_threshold" value { string_value: "com.spotify.gaia=30,driving-mode=120,spotify%3Ainternal%3Astartpage=30" } } account_attributes { key: "license-agreements" value { string_value: "" } } account_attributes { key: "publish-playlist" value { bool_value: false } } account_attributes { key: "buffering-strategy" value { string_value: "0" } } account_attributes { key: "expiry" value { bool_value: true } } account_attributes { key: "ab-desktop-hide-follow" value { bool_value: false } } account_attributes { key: "ab-collection-hide-unavailable-albums" value { bool_value: false } } account_attributes { key: "payments-initial-campaign" value { string_value: "web" } } account_attributes { key: "capper-profile" value { string_value: "" } } account_attributes { key: "profile-image-upload" value { string_value: "1" } } account_attributes { key: "enable-annotations" value { string_value: "2" } } account_attributes { key: "ab-ad-player-targeting" value { bool_value: true } } account_attributes { key: "india-experience" value { string_value: "0" } } account_attributes { key: "video-wifi-initial-bitrate" value { long_value: 800000 } } account_attributes { key: "ab-mobile-running-onlymanualmode" value { string_value: "only-manual" } } account_attributes { key: "playlist-annotations-markup" value { bool_value: false } } account_attributes { key: "prefetch-keys" value { string_value: "1" } } account_attributes { key: "allow-override-internal-prefs" value { bool_value: false } } account_attributes { key: "prefetch-window-max" value { long_value: 2 } } account_attributes { key: "send-email" value { bool_value: false } } account_attributes { key: "ad-formats-video-takeover" value { bool_value: true } } account_attributes { key: "npt-disabled" value { string_value: "2" } } account_attributes { key: "ab-collection-bookmark-model" value { bool_value: true } } account_attributes { key: "pause-after" value { long_value: 0 } } account_attributes { key: "local-files-import" value { string_value: "0" } } account_attributes { key: "video-manifest-url" value { string_value: "https://spclient.wg.spotify.com/manifests/v6/{type}/sources/{source_id}/options/supports_drm" } } account_attributes { key: "playlist-folders" value { bool_value: true } } account_attributes { key: "ab-sugarpills-sanity-check" value { string_value: "0" } } account_attributes { key: "arsenal_country" value { bool_value: true } } account_attributes { key: "track-cap" value { bool_value: false } } account_attributes { key: "ab-mobile-running-tempo-detection" value { string_value: "Control" } } account_attributes { key: "shows-collection" value { bool_value: true } } account_attributes { key: "is_email_verified" value { bool_value: true } } account_attributes { key: "enable-annotations-read" value { bool_value: false } } account_attributes { key: "addon-hifi" value { bool_value: false } } account_attributes { key: "mobile-browse" value { bool_value: false } } account_attributes { key: "payments-locked-state" value { bool_value: false } } account_attributes { key: "shows-collection-jam" value { bool_value: true } } account_attributes { key: "ab-browse-music-tuesday" value { bool_value: true } } account_attributes { key: "offline" value { bool_value: true } } account_attributes { key: "streaming" value { bool_value: true } } account_attributes { key: "fb-info-confirmation" value { string_value: "control" } } account_attributes { key: "audio-quality" value { string_value: "1" } } account_attributes { key: "taste-onboarding-disabled" value { bool_value: false } } account_attributes { key: "wanted-licenses" value { string_value: "" } } account_attributes { key: "lastfm-session" value { string_value: "b87...039|sashahilton00" } } account_attributes { key: "ab-desktop-playlist-annotation-edit" value { long_value: 1 } } account_attributes { key: "ab-mobile-discover" value { long_value: 0 } } account_attributes { key: "widevine-license-url" value { string_value: "https://spclient.wg.spotify.com/widevine-license/v1/video/license" } } account_attributes { key: "key-caching-max-count" value { long_value: 10000 } } account_attributes { key: "ad-session-persistence" value { bool_value: true } } account_attributes { key: "video-initial-bitrate" value { string_value: "200000" } } account_attributes { key: "filter-explicit-content" value { bool_value: false } } account_attributes { key: "ab-play-history" value { bool_value: false } } account_attributes { key: "payment-state" value { string_value: "" } } account_attributes { key: "mobile-payment" value { string_value: "0" } } account_attributes { key: "key-caching-max-offline-seconds" value { long_value: 1800 } } account_attributes { key: "ugc-abuse-report-url" value { string_value: "https://support.spotify.com/abuse/?uri={uri}" } } account_attributes { key: "shuffle-algorithm" value { bool_value: true } } account_attributes { key: "use-pl3" value { bool_value: false } } account_attributes { key: "image-url" value { string_value: "https://i.scdn.co/image/{file_id}" } } account_attributes { key: "use-playlist-app" value { bool_value: false } } account_attributes { key: "metadata-link-lookup-modes" value { bool_value: false } } account_attributes { key: "enable-autostart" value { bool_value: true } } account_attributes { key: "shuffle" value { bool_value: false } } account_attributes { key: "instant-search" value { bool_value: false } } account_attributes { key: "instant-search-expand-sidebar" value { bool_value: false } } account_attributes { key: "use-playlist-uris" value { bool_value: false } } account_attributes { key: "user-profile-show-invitation-codes" value { bool_value: false } } account_attributes { key: "ab-ad-requester" value { bool_value: true } } account_attributes { key: "nft-disabled" value { string_value: "1" } } account_attributes { key: "payments-cancel-state-interstitial" value { bool_value: false } } account_attributes { key: "ab-collection-union" value { bool_value: true } } account_attributes { key: "libspotify" value { bool_value: true } } account_attributes { key: "key-memory-cache-mode" value { string_value: "1:15,300" } } account_attributes { key: "high-bitrate" value { bool_value: true } } account_attributes { key: "head-file-caching" value { bool_value: true } } account_attributes { key: "radio" value { bool_value: true } } account_attributes { key: "key-caching-auto-offline" value { bool_value: false } } account_attributes { key: "explicit-content" value { bool_value: true } } account_attributes { key: "video-cdn-sampling" value { long_value: 1 } } account_attributes { key: "ab-android-push-notifications" value { bool_value: true } } account_attributes { key: "unrestricted" value { bool_value: true } } account_attributes { key: "use-fb-publish-backend" value { long_value: 2 } } account_attributes { key: "app-developer" value { long_value: 1 } } account_attributes { key: "enable-gapless" value { bool_value: true } } account_attributes { key: "buffering-strategy-parameters" value { string_value: "0.8:0.2:0.0:0.0:0.0:0.0:1.0:10:10:2000:10000:10485760" } } account_attributes { key: "ab-playlist-extender" value { long_value: 5 } } account_attributes { key: "enable-crossfade" value { bool_value: true } } account_attributes { key: "ad-persist-reward-time" value { bool_value: false } } account_attributes { key: "public-toplist" value { string_value: "2" } } account_attributes { key: "network-operator-premium-activation" value { bool_value: true } } account_attributes { key: "request-time" value { string_value: "6" } } account_attributes { key: "video-device-blacklisted" value { bool_value: false } } account_attributes { key: "collection" value { bool_value: true } } account_attributes { key: "fb-grant-permission-local-render" value { bool_value: false } } account_attributes { key: "backend-advised-bitrate" value { bool_value: true } } account_attributes { key: "catalogue" value { string_value: "premium" } } account_attributes { key: "storage-size-config" value { string_value: "10240,90,500,3" } } account_attributes { key: "ads" value { bool_value: false } } account_attributes { key: "head-files-url" value { string_value: "https://heads-fa.scdn.co/head/{file_id}" } } account_attributes { key: "browse-overview-enabled" value { bool_value: true } } account_attributes { key: "ab-collection-followed-artists-only" value { bool_value: false } } account_attributes { key: "on-demand" value { bool_value: true } } account_attributes { key: "ap-resolve-pods" value { string_value: "0" } } account_attributes { key: "name" value { string_value: "Spotify Premium" } } account_attributes { key: "sidebar-navigation-enabled" value { bool_value: false } } account_attributes { key: "loudness-levels" value { string_value: "1:-9.0,0.0,3.0:-2.0" } } account_attributes { key: "mobile-login" value { bool_value: true } } account_attributes { key: "preferred-locale" value { string_value: "en-gb" } } account_attributes { key: "license-acceptance-grace-days" value { long_value: 30 } } account_attributes { key: "prefetch-strategy" value { long_value: 2 } } account_attributes { key: "ugc-abuse-report" value { bool_value: true } } account_attributes { key: "ab-watch-now" value { bool_value: false } } account_attributes { key: "payments-latest-reusable-provider" value { string_value: "paypal;2020-06-15" } } account_attributes { key: "financial-product" value { string_value: "pr:premium,tc:0" } } account_attributes { key: "ab-collection-offline-mode" value { bool_value: false } } account_attributes { key: "streaming-rules" value { string_value: "" } } account_attributes { key: "head-files" value { bool_value: true } } account_attributes { key: "capping-bar-threshold" value { long_value: 3601 } } account_attributes { key: "ab-new-share-flow" value { bool_value: false } } account_attributes { key: "video-keyframe-url" value { string_value: "http://keyframes-fa.cdn.spotify.com/keyframes/v1/sources/{source_id}/keyframe/heights/{height}/timestamps/{timestamp_ms}.jpg" } } account_attributes { key: "mobile" value { bool_value: true } } account_attributes { key: "is_maybe_in_social_session" value { bool_value: false } } account_attributes { key: "ab-mobile-startpage" value { long_value: 0 } } account_attributes { key: "ab-moments-experience" value { string_value: "0" } } account_attributes { key: "ab-mobile-social-feed" value { string_value: "1" } } account_attributes { key: "ab-test-group" value { long_value: 972 } } account_attributes { key: "player-license" value { string_value: "premium" } } account_attributes { key: "country_code" value { string_value: "GB" } } account_attributes { key: "com.spotify.madprops.delivered.by.ucs" value { bool_value: true } } account_attributes { key: "com.spotify.madprops.use.ucs.product.state" value { bool_value: false } } } fetch_time_millis: 1620515556953 } ``` </details>
Author
Owner

@sashahilton00 commented on GitHub (May 9, 2021):

Connect State Endpoint

Endpoint: https://spclient.wg.spotify.com/connect-state/v1/devices/2e8...202

Proto definitions (connect.proto):

  • Request: This requires further investigation. The content that is sent appears to be either compressed, encrypted or obfuscated, unless I am missing something obvious. It doesn't have anything that stands out as a proto message.
  • Response: The response seems to be a connectstate.Cluster message, though I want to double check this. It looks pretty similar to the web api connect state endpoint, there may be a few additional fields.

Notes

To implement the repeat single/context functonality on the client, one needs to look at the PlayerState (player.proto) entry of connectstate.Cluster. Within that there is a ContextPlayerOptions entry, which indicates whether repeat functionality is enabled and what type.

ContextRestrictions can also be seen in player state, and should be used to determine what functionality is enabled. For example, the disallow_pausing_reasons context restriction sometimes appears if the player is paused, with the somewhat self-explanatory already_paused message. Granted, not a very useful example, but we should probably pay attention to these context restrictions and pass through the reason for disabling either to stdout or the application using librespot. Example below:

restrictions {
    disallow_pausing_reasons: "already_paused"
    disallow_skipping_prev_reasons: "no_prev_track"
  }

Capabilities also has some new fields that indicate whether a device supports Spotify HiFi. There are some other fields within connectstate.CapabilitySupportDetails such as fully_supported and user_eligible that presumably need to be set for the option to appear. See below:

supports_hifi {
        device_supported: true
        fully_supported: false
        user_eligible: false
      }

Also, there are some other fields within the device message such as metadata_map that look to be the local network details of the client, notably device_address_mask and tier1_port, both of which appear to correspond to the mDNS/SSDP discovery that spotify clients do. It might be worth investigating adding this information to boost speed and reliability of device discovery.

The device-state endpoint needs quite a bit more exploration, and probably warrants a thread of its own, as it seems to be the bucket into which Spotify is dumping most of the player state used when syncing up devices.

<!-- gh-comment-id:835610153 --> @sashahilton00 commented on GitHub (May 9, 2021): # Connect State Endpoint Endpoint: `https://spclient.wg.spotify.com/connect-state/v1/devices/2e8...202` Proto definitions (`connect.proto`): - Request: This requires further investigation. The content that is sent appears to be either compressed, encrypted or obfuscated, unless I am missing something obvious. It doesn't have anything that stands out as a proto message. - Response: The response seems to be a `connectstate.Cluster` message, though I want to double check this. It looks pretty similar to the web api connect state endpoint, there may be a few additional fields. ## Notes To implement the repeat single/context functonality on the client, one needs to look at the `PlayerState` (player.proto) entry of `connectstate.Cluster`. Within that there is a `ContextPlayerOptions` entry, which indicates whether repeat functionality is enabled and what type. `ContextRestrictions` can also be seen in player state, and should be used to determine what functionality is enabled. For example, the `disallow_pausing_reasons` context restriction sometimes appears if the player is paused, with the somewhat self-explanatory `already_paused` message. Granted, not a very useful example, but we should probably pay attention to these context restrictions and pass through the reason for disabling either to stdout or the application using librespot. Example below: ``` restrictions { disallow_pausing_reasons: "already_paused" disallow_skipping_prev_reasons: "no_prev_track" } ``` `Capabilities` also has some new fields that indicate whether a device supports Spotify HiFi. There are some other fields within `connectstate.CapabilitySupportDetails` such as `fully_supported` and `user_eligible` that presumably need to be set for the option to appear. See below: ``` supports_hifi { device_supported: true fully_supported: false user_eligible: false } ``` Also, there are some other fields within the device message such as `metadata_map` that look to be the local network details of the client, notably `device_address_mask` and `tier1_port`, both of which appear to correspond to the mDNS/SSDP discovery that spotify clients do. It might be worth investigating adding this information to boost speed and reliability of device discovery. The device-state endpoint needs quite a bit more exploration, and probably warrants a thread of its own, as it seems to be the bucket into which Spotify is dumping most of the player state used when syncing up devices.
Author
Owner

@roderickvd commented on GitHub (May 11, 2021):

    account_attributes {
      key: "loudness-levels"
      value {
        string_value: "1:-9.0,0.0,3.0:-2.0"
      }

The comma-separated values probably map to Spotify's loudness levels. This is old documentation from the net:

Loud – equalling ca -11 dB LUFS (+6 dB gain multiplied to ReplayGain)
Normal (default) – equalling ca -14 dB LUFS (+3 dB gain multiplied to ReplayGain)
Quiet – equalling ca – 23 dB LUFS (-5 dB gain multiplied to ReplayGain)

Currently Spotify only documents the LUFS value and no longer the dB gain. Where dB is assumed to be dBFS and LUFS is k-weighted. So the documentation above is indicative but not necessarily normative.

If you add 3 dB to the comma-separated you're more-or-less meeting the indicative documented values.

I'd have to think harder what the first "1" and last "-2.0" indicate. Taking a guess: "1" for the scheme version number and "-2.0" for the -2 dB true peak that Spotify targets.

@sashahilton00 general question: so I understand librespot-java has implemented large parts of the new API. What can we reuse from their reverse engineering vs. your posts above? I'm just entirely new to these parts so trying to understand the lay of the land here.

<!-- gh-comment-id:838475964 --> @roderickvd commented on GitHub (May 11, 2021): ``` account_attributes { key: "loudness-levels" value { string_value: "1:-9.0,0.0,3.0:-2.0" } ``` The comma-separated values probably map to Spotify's loudness levels. This is old documentation from the net: > Loud – equalling ca -11 dB LUFS (+6 dB gain multiplied to ReplayGain) > Normal (default) – equalling ca -14 dB LUFS (+3 dB gain multiplied to ReplayGain) > Quiet – equalling ca – 23 dB LUFS (-5 dB gain multiplied to ReplayGain) Currently Spotify only documents the LUFS value and no longer the dB gain. Where dB is assumed to be dBFS and LUFS is k-weighted. So the documentation above is indicative but not necessarily normative. If you add 3 dB to the comma-separated you're more-or-less meeting the indicative documented values. I'd have to think harder what the first "1" and last "-2.0" indicate. Taking a guess: "1" for the scheme version number and "-2.0" for the -2 dB true peak that Spotify targets. @sashahilton00 general question: so I understand `librespot-java` has implemented large parts of the new API. What can we reuse from their reverse engineering vs. your posts above? I'm just entirely new to these parts so trying to understand the lay of the land here.
Author
Owner

@devgianlu commented on GitHub (May 11, 2021):

Ping me if you need any help.

<!-- gh-comment-id:838482525 --> @devgianlu commented on GitHub (May 11, 2021): Ping me if you need any help.
Author
Owner

@sashahilton00 commented on GitHub (May 11, 2021):

@roderickvd that's a good question. From the issue emails I've been getting from librespot-java, it looks like @devgianlu and co. have put quite a bit of work into reverse engineering chunks of the new API, hence in some cases the stuff I post here has likely been reverse engineered in part or fully over at librespot-java. @devgianlu would be best placed to answer the question of what has been reverse engineered that we can use, something of note is the dealer endpoint, which if we want to support group listening at some point, will need to be added, as thisis where group listening context seems to be published to. Other than that, it just sends pingpong requests unless I am missing something.

My reverse engineering is simply me capturing and examining the network traffic between desktop <-> Spotify, so what I post should be useful enough as a reference, but in terms of what we focus on, we should probably look to discussions and their upvotes, along with features that we thnk are going to be increasingly important when working out what to prioritise.

In my mind, there are a few things that are must haves before the library hits 1.0.0, which are:

  • removal of surplus audio backends. Currently my thinking is that retaining the pipe, rodio (default), alsa and gstreamer backends makes sense, on the understanding that the alsaand gstreamer backends are being actively maintained. Given the processing that we are already doing with normalisation, dithering, shaping, etc. combined with the slow movement on issues upstream in rodio, I am inclined to think that it may be worth dropping rodio in favour of cpal as a lighter alternative, given that we're not using the functionality that rodio provides (multi format decoding, dynamic mixing, filters, etc.). If we feel that this is going to change, for example with the introduction of HiFi, then perhaps this is worth reevaluating.

  • remove redundant decoders. tremor and libvorbis should both be removed I think. The former because it was introduced iirc for the Pi rev 1, which didn't have a hardware FPU. The latter because it a) relies on bndings to a C library, and b) the crate appears unmaintained.

  • support session reconnection. This is being tracked in #609 but requires a level of rust experience that exceeds my own. The reason for this being a requirement in my mind is that there's a trove of issues that appear to be connected to session reconnection not really being handled, which one wouldn't really consider acceptable in a 'stable' library.

  • retrieve files from the CDN. Again, not technically a requirement, but from what I've seen in recent additions such as canvas, podcasts, etc. these are defaulting to being supplied via the CDN, and there is clearly a push to move to use the CDN at Spotify for all new projects. Besides avoiding the nasty surprise when Spotify eventually decides to turn off the mercury endpoint one day, there should be a small performance boost in that audio files will be served from edge locations as opposed to from Spotify servers, which may help reduce overall load times and responsiveness.

  • there has been plenty of discussion in #648 around splitting out discovery functionality, moving audio playback and other features to Spotifyd, etc. If we are going to do that, it should be done before we hit stable. Personally I am in favour of moving most of the playback functionality to Spotifyd, and only retaining the pipe and cpal backends in librespot, though multiple people have said that they want alsa and gstreamer in librespot and are willing to maintain them, n which case I don't think forcing migration is necessary.

Besides the above, there are other things that would be 'nice to have', but are by no means essential. Things like extended metadata can be added in later versions, the above is just some of the stuff which I think is required for it to be released as stable. If people have a different view, I'm open to changing my mind. In the meantime, I will keep documenting the protocols when I have a moment and adding them here, since it's just grunt work that I can do when I have an hour or two spare. Anyone else feel free to do the same.

<!-- gh-comment-id:838589933 --> @sashahilton00 commented on GitHub (May 11, 2021): @roderickvd that's a good question. From the issue emails I've been getting from librespot-java, it looks like @devgianlu and co. have put quite a bit of work into reverse engineering chunks of the new API, hence in some cases the stuff I post here has likely been reverse engineered in part or fully over at librespot-java. @devgianlu would be best placed to answer the question of what has been reverse engineered that we can use, something of note is the dealer endpoint, which if we want to support group listening at some point, will need to be added, as thisis where group listening context seems to be published to. Other than that, it just sends pingpong requests unless I am missing something. My reverse engineering is simply me capturing and examining the network traffic between desktop <-> Spotify, so what I post should be useful enough as a reference, but in terms of what we focus on, we should probably look to discussions and their upvotes, along with features that we thnk are going to be increasingly important when working out what to prioritise. In my mind, there are a few things that are must haves before the library hits `1.0.0`, which are: - removal of surplus audio backends. Currently my thinking is that retaining the `pipe`, `rodio` (default), `alsa` and `gstreamer` backends makes sense, on the understanding that the `alsa`and `gstreamer` backends are being actively maintained. Given the processing that we are already doing with normalisation, dithering, shaping, etc. combined with the slow movement on issues upstream in rodio, I am inclined to think that it may be worth dropping rodio in favour of cpal as a lighter alternative, given that we're not using the functionality that rodio provides (multi format decoding, dynamic mixing, filters, etc.). If we feel that this is going to change, for example with the introduction of HiFi, then perhaps this is worth reevaluating. - remove redundant decoders. `tremor` and `libvorbis` should both be removed I think. The former because it was introduced iirc for the Pi rev 1, which didn't have a hardware FPU. The latter because it a) relies on bndings to a C library, and b) the crate appears unmaintained. - support session reconnection. This is being tracked in #609 but requires a level of rust experience that exceeds my own. The reason for this being a requirement in my mind is that there's a trove of issues that appear to be connected to session reconnection not really being handled, which one wouldn't really consider acceptable in a 'stable' library. - retrieve files from the CDN. Again, not technically a requirement, but from what I've seen in recent additions such as canvas, podcasts, etc. these are defaulting to being supplied via the CDN, and there is clearly a push to move to use the CDN at Spotify for all new projects. Besides avoiding the nasty surprise when Spotify eventually decides to turn off the mercury endpoint one day, there should be a small performance boost in that audio files will be served from edge locations as opposed to from Spotify servers, which may help reduce overall load times and responsiveness. - there has been plenty of discussion in #648 around splitting out discovery functionality, moving audio playback and other features to Spotifyd, etc. If we are going to do that, it should be done before we hit stable. Personally I am in favour of moving most of the playback functionality to Spotifyd, and only retaining the `pipe` and `cpal` backends in librespot, though multiple people have said that they want `alsa` and `gstreamer` in librespot and are willing to maintain them, n which case I don't think forcing migration is necessary. Besides the above, there are other things that would be 'nice to have', but are by no means essential. Things like extended metadata can be added in later versions, the above is just some of the stuff which I think is required for it to be released as stable. If people have a different view, I'm open to changing my mind. In the meantime, I will keep documenting the protocols when I have a moment and adding them here, since it's just grunt work that I can do when I have an hour or two spare. Anyone else feel free to do the same.
Author
Owner

@sashahilton00 commented on GitHub (May 11, 2021):

Looking back, the above is a bit of a roaming response, but hopefully it provides some clarification on why I'm spending the time to examine the latest protocols and the overall direction that I'm working to as a basis.

<!-- gh-comment-id:838593837 --> @sashahilton00 commented on GitHub (May 11, 2021): Looking back, the above is a bit of a roaming response, but hopefully it provides some clarification on why I'm spending the time to examine the latest protocols and the overall direction that I'm working to as a basis.
Author
Owner

@sashahilton00 commented on GitHub (May 11, 2021):

Client Token Endpoint (Protobuf)

Endpoint: POST https://clienttoken.spotify.com/v1/clienttoken

Proto Definitions missing.

Notes

This endpoint is where the client token is retrieved from, which is subsequently used in a multitude of requests. The request/response are shown below. Further investigation is required to determne the proto definitions, which will be updated accordingly in the request/response once discovered.

65b7... appears to be a Spotify Client ID. This is also used in the login5 endpoint.

Request

1: 1
2 {
  1: "1.1.58.820.g2ae50076"
  2: "65b7080...33ca87bd"
  3 {
    1 {
      4 {
        1: 10
        3: 17763
        4: 2
        6: 9
        7: 332
        8: 34404
      }
    }
    2: "S-1-5-21-4265178016-72824351-3788689935"
  }
}

Response

1: 1
2 {
  1: "AABlJC8n81KZ3mO8VAbi9H9B6...z2FjzT+gISTWoyP0="
  2: 1216800
  3: 1209600
  4 {
    1: "spotify.com"
  }
}```
<!-- gh-comment-id:838873780 --> @sashahilton00 commented on GitHub (May 11, 2021): ## Client Token Endpoint (Protobuf) Endpoint: `POST https://clienttoken.spotify.com/v1/clienttoken` Proto Definitions missing. ### Notes This endpoint is where the client token is retrieved from, which is subsequently used in a multitude of requests. The request/response are shown below. Further investigation is required to determne the proto definitions, which will be updated accordingly in the request/response once discovered. `65b7...` appears to be a Spotify Client ID. This is also used in the login5 endpoint. ### Request ``` 1: 1 2 { 1: "1.1.58.820.g2ae50076" 2: "65b7080...33ca87bd" 3 { 1 { 4 { 1: 10 3: 17763 4: 2 6: 9 7: 332 8: 34404 } } 2: "S-1-5-21-4265178016-72824351-3788689935" } } ``` ### Response ``` 1: 1 2 { 1: "AABlJC8n81KZ3mO8VAbi9H9B6...z2FjzT+gISTWoyP0=" 2: 1216800 3: 1209600 4 { 1: "spotify.com" } }```
Author
Owner

@devgianlu commented on GitHub (May 11, 2021):

@sashahilton00 Should this become a discussion? I feel like I could comment on stuff, but it'll end up being an awful long thread.

<!-- gh-comment-id:838885923 --> @devgianlu commented on GitHub (May 11, 2021): @sashahilton00 Should this become a discussion? I feel like I could comment on stuff, but it'll end up being an awful long thread.
Author
Owner

@sashahilton00 commented on GitHub (May 11, 2021):

I thought it already was, just realised it's still in issues. Will move it.

<!-- gh-comment-id:838889941 --> @sashahilton00 commented on GitHub (May 11, 2021): I thought it already was, just realised it's still in issues. Will move it.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/librespot#380
No description provided.