11/*
2- * Copyright 2021 devgianlu
2+ * Copyright 2022 devgianlu
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1717package xyz .gianlu .librespot .dealer ;
1818
1919import com .google .protobuf .Message ;
20+ import com .spotify .clienttoken .data .v0 .Connectivity ;
21+ import com .spotify .clienttoken .http .v0 .ClientToken ;
2022import com .spotify .connectstate .Connect ;
2123import com .spotify .extendedmetadata .ExtendedMetadata ;
2224import com .spotify .metadata .Metadata ;
2628import org .jetbrains .annotations .Nullable ;
2729import org .slf4j .Logger ;
2830import org .slf4j .LoggerFactory ;
29- import xyz .gianlu .librespot .core . ApResolver ;
31+ import xyz .gianlu .librespot .Version ;
3032import xyz .gianlu .librespot .core .Session ;
3133import xyz .gianlu .librespot .mercury .MercuryClient ;
34+ import xyz .gianlu .librespot .mercury .MercuryRequests ;
3235import xyz .gianlu .librespot .metadata .*;
3336
3437import java .io .IOException ;
@@ -43,6 +46,7 @@ public final class ApiClient {
4346 private static final Logger LOGGER = LoggerFactory .getLogger (ApiClient .class );
4447 private final Session session ;
4548 private final String baseUrl ;
49+ private String clientToken = null ;
4650
4751 public ApiClient (@ NotNull Session session ) {
4852 this .session = session ;
@@ -54,7 +58,7 @@ public static RequestBody protoBody(@NotNull Message msg) {
5458 return new RequestBody () {
5559 @ Override
5660 public MediaType contentType () {
57- return MediaType .get ("application/protobuf" );
61+ return MediaType .get ("application/x- protobuf" );
5862 }
5963
6064 @ Override
@@ -66,10 +70,17 @@ public void writeTo(@NotNull BufferedSink sink) throws IOException {
6670
6771 @ NotNull
6872 private Request buildRequest (@ NotNull String method , @ NotNull String suffix , @ Nullable Headers headers , @ Nullable RequestBody body ) throws IOException , MercuryClient .MercuryException {
73+ if (clientToken == null ) {
74+ ClientToken .ClientTokenResponse resp = clientToken ();
75+ clientToken = resp .getGrantedToken ().getToken ();
76+ LOGGER .debug ("Updated client token: {}" , clientToken );
77+ }
78+
6979 Request .Builder request = new Request .Builder ();
7080 request .method (method , body );
7181 if (headers != null ) request .headers (headers );
7282 request .addHeader ("Authorization" , "Bearer " + session .tokens ().get ("playlist-read" ));
83+ request .addHeader ("client-token" , clientToken );
7384 request .url (baseUrl + suffix );
7485 return request .build ();
7586 }
@@ -201,6 +212,49 @@ public ExtendedMetadata.BatchedExtensionResponse getExtendedMetadata(@NotNull Ex
201212 }
202213 }
203214
215+ @ NotNull
216+ private ClientToken .ClientTokenResponse clientToken () throws IOException {
217+ ClientToken .ClientTokenRequest protoReq = ClientToken .ClientTokenRequest .newBuilder ()
218+ .setRequestType (ClientToken .ClientTokenRequestType .REQUEST_CLIENT_DATA_REQUEST )
219+ .setClientData (ClientToken .ClientDataRequest .newBuilder ()
220+ .setClientId (MercuryRequests .KEYMASTER_CLIENT_ID )
221+ .setClientVersion (Version .versionNumber ())
222+ .setConnectivitySdkData (Connectivity .ConnectivitySdkData .newBuilder ()
223+ .setDeviceId (session .deviceId ())
224+ .setPlatformSpecificData (Connectivity .PlatformSpecificData .newBuilder ()
225+ .setWindows (Connectivity .NativeWindowsData .newBuilder ()
226+ .setSomething1 (10 )
227+ .setSomething3 (21370 )
228+ .setSomething4 (2 )
229+ .setSomething6 (9 )
230+ .setSomething7 (332 )
231+ .setSomething8 (34404 )
232+ .setSomething10 (true )
233+ .build ())
234+ .build ())
235+ .build ())
236+ .build ())
237+ .build ();
238+
239+ Request .Builder req = new Request .Builder ()
240+ .url ("https://clienttoken.spotify.com/v1/clienttoken" )
241+ .header ("Accept" , "application/x-protobuf" )
242+ .header ("Content-Encoding" , "" )
243+ .post (protoBody (protoReq ));
244+
245+ try (Response resp = session .client ().newCall (req .build ()).execute ()) {
246+ StatusCodeException .checkStatus (resp );
247+
248+ ResponseBody body = resp .body ();
249+ if (body == null ) throw new IOException ();
250+ return ClientToken .ClientTokenResponse .parseFrom (body .byteStream ());
251+ }
252+ }
253+
254+ public void setClientToken (@ Nullable String clientToken ) {
255+ this .clientToken = clientToken ;
256+ }
257+
204258 public static class StatusCodeException extends IOException {
205259 public final int code ;
206260
0 commit comments