librespot-org / librespot

Open Source Spotify client library
MIT License
4.86k stars 616 forks source link

Update protobuf definitions and endpoints to use current official client ones. #650

Closed sashahilton00 closed 3 years ago

sashahilton00 commented 3 years ago

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

Johannesd3 commented 3 years ago

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.

xou816 commented 3 years ago

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?

sashahilton00 commented 3 years ago

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

sashahilton00 commented 3 years ago

Extended Metadata Endpoint (Protobuf)

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

Proto definitions (refer to extended_metadata.proto):

Decoded Request Screenshot

cap_extendedmetadata_request

Decoded Response Screenshot

cap_extendedmetadata_response

sashahilton00 commented 3 years ago

User Customisation Endpoint (Protobuf)

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

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

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 } ```
sashahilton00 commented 3 years ago

Connect State Endpoint

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

Proto definitions (connect.proto):

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.

roderickvd commented 3 years ago
    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.

devgianlu commented 3 years ago

Ping me if you need any help.

sashahilton00 commented 3 years ago

@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:

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.

sashahilton00 commented 3 years ago

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.

sashahilton00 commented 3 years ago

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"
  }
}```
devgianlu commented 3 years ago

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

sashahilton00 commented 3 years ago

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