Skip to content

Commit 49c2913

Browse files
committed
refactor
1 parent 8580280 commit 49c2913

File tree

4 files changed

+161
-173
lines changed

4 files changed

+161
-173
lines changed

extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/ClientTokenFetcher.java

Lines changed: 0 additions & 137 deletions
This file was deleted.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package app.revanced.extension.spotify.misc.fix;
2+
3+
import androidx.annotation.NonNull;
4+
import app.revanced.extension.shared.Logger;
5+
import app.revanced.extension.spotify.clienttoken.data.v0.ClienttokenHttp.*;
6+
7+
import java.io.IOException;
8+
import java.security.PrivateKey;
9+
10+
import static app.revanced.extension.spotify.misc.fix.Constants.*;
11+
12+
class ClientTokenService {
13+
private static final String IOS_CLIENT_ID = "58bd3c95768941ea9eb4350aaa033eb3";
14+
15+
private static final ConnectivitySdkData IOS_CONNECTIVITY_SDK_DATA =
16+
ConnectivitySdkData.newBuilder()
17+
.setPlatformSpecificData(PlatformSpecificData.newBuilder()
18+
.setIos(NativeIOSData.newBuilder()
19+
.setHwMachine(getHardwareMachine())
20+
.setSystemVersion(getSystemVersion())
21+
.build())
22+
.build())
23+
.build();
24+
25+
private static final ClientDataRequest IOS_CLIENT_DATA_REQUEST =
26+
ClientDataRequest.newBuilder()
27+
.setClientVersion(getClientVersion())
28+
.setClientId(IOS_CLIENT_ID)
29+
.build();
30+
31+
private static final ClientTokenRequest IOS_CLIENT_TOKEN_REQUEST =
32+
ClientTokenRequest.newBuilder()
33+
.setRequestType(ClientTokenRequestType.REQUEST_CLIENT_DATA_REQUEST)
34+
.build();
35+
36+
37+
@NonNull
38+
static ClientTokenRequest newIOSClientTokenRequest(String deviceId) {
39+
Logger.printInfo(() -> "Creating new iOS client token request with device ID: " + deviceId);
40+
return IOS_CLIENT_TOKEN_REQUEST
41+
.toBuilder()
42+
.setClientData(IOS_CLIENT_DATA_REQUEST
43+
.toBuilder()
44+
.setConnectivitySdkData(IOS_CONNECTIVITY_SDK_DATA
45+
.toBuilder()
46+
.setDeviceId(deviceId)
47+
)
48+
).build();
49+
}
50+
51+
interface ClientTokenRequestHandler {
52+
@NonNull
53+
ClientTokenResponse request(@NonNull ClientTokenRequest request) throws IOException;
54+
}
55+
56+
static ClientTokenResponse getClientTokenResponse(
57+
@NonNull ClientTokenRequest request,
58+
@NonNull ClientTokenRequestHandler handler
59+
) {
60+
if (request.getRequestType() == ClientTokenRequestType.REQUEST_CLIENT_DATA_REQUEST) {
61+
String deviceId = request.getClientData().getConnectivitySdkData().getDeviceId();
62+
request = newIOSClientTokenRequest(deviceId);
63+
} else {
64+
Logger.printInfo(() -> "Client token request type is not REQUEST_CLIENT_DATA_REQUEST");
65+
return null;
66+
}
67+
68+
ClientTokenResponse clientTokenResponse;
69+
try {
70+
clientTokenResponse = handler.request(request);
71+
} catch (IOException ex) {
72+
Logger.printException(() -> "Failed to request client token", ex);
73+
return null;
74+
}
75+
76+
if (clientTokenResponse.getResponseType() != ClientTokenResponseType.RESPONSE_GRANTED_TOKEN_RESPONSE) {
77+
Logger.printException(() -> "Unexpected client token response type: " +
78+
clientTokenResponse.getResponseType());
79+
return null;
80+
}
81+
82+
return clientTokenResponse;
83+
}
84+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package app.revanced.extension.spotify.misc.fix;
2+
3+
import androidx.annotation.NonNull;
4+
5+
class Constants {
6+
7+
// Modified by a patch. Do not touch.
8+
@NonNull
9+
static String getClientVersion() {
10+
return "";
11+
}
12+
13+
// Modified by a patch. Do not touch.
14+
@NonNull
15+
static String getSystemVersion() {
16+
return "";
17+
}
18+
19+
// Modified by a patch. Do not touch.
20+
@NonNull
21+
static String getHardwareMachine() {
22+
return "";
23+
}
24+
}

extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/fix/RequestListener.java

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,35 @@
22

33
import androidx.annotation.NonNull;
44
import app.revanced.extension.shared.Logger;
5-
import app.revanced.extension.spotify.clienttoken.data.v0.ClienttokenHttp;
5+
import app.revanced.extension.spotify.clienttoken.data.v0.ClienttokenHttp.*;
66
import com.google.protobuf.MessageLite;
77
import fi.iki.elonen.NanoHTTPD;
88

99
import java.io.ByteArrayInputStream;
1010
import java.io.FilterInputStream;
1111
import java.io.IOException;
1212
import java.io.InputStream;
13+
import java.net.HttpURLConnection;
14+
import java.net.URL;
1315
import java.util.Objects;
1416

17+
import static app.revanced.extension.spotify.misc.fix.ClientTokenService.*;
18+
import static app.revanced.extension.spotify.misc.fix.Constants.*;
1519
import static fi.iki.elonen.NanoHTTPD.Response.Status.INTERNAL_ERROR;
1620

1721
class RequestListener extends NanoHTTPD {
22+
private static final String CLIENT_TOKEN_API_PATH = "/v1/clienttoken";
23+
private static final String CLIENT_TOKEN_API_URL = "https://clienttoken.spotify.com" + CLIENT_TOKEN_API_PATH;
24+
25+
private static final String IOS_USER_AGENT;
26+
27+
static {
28+
String clientVersion = getClientVersion();
29+
String version = clientVersion.substring(clientVersion.indexOf("-") + 1, clientVersion.lastIndexOf("."));
30+
31+
IOS_USER_AGENT = "Spotify/" + version + " iOS/" + getSystemVersion() + " (" + getHardwareMachine() + ")";
32+
}
33+
1834
RequestListener(int port) {
1935
super(port);
2036

@@ -26,49 +42,48 @@ class RequestListener extends NanoHTTPD {
2642
}
2743
}
2844

45+
2946
@NonNull
3047
@Override
31-
public Response serve(@NonNull IHTTPSession request) {
32-
String uri = request.getUri();
48+
public Response serve(IHTTPSession session) {
49+
String uri = session.getUri();
3350
Logger.printInfo(() -> "Serving request for URI: " + uri);
51+
if (!uri.equals(CLIENT_TOKEN_API_PATH)) return INTERNAL_ERROR_RESPONSE;
3452

35-
if (!uri.equals(ClientTokenFetcher.CLIENT_TOKEN_API_PATH)) return newResponse(INTERNAL_ERROR);
36-
37-
InputStream requestBodyInputStream = getRequestBodyInputStream(request);
38-
39-
ClienttokenHttp.ClientTokenRequest clientTokenRequest;
40-
try {
41-
clientTokenRequest = ClienttokenHttp.ClientTokenRequest.parseFrom(requestBodyInputStream);
53+
ClientTokenRequest clientTokenRequest;
54+
try (InputStream inputStream = getInputStream(session)) {
55+
clientTokenRequest = ClientTokenRequest.parseFrom(inputStream);
4256
} catch (IOException ex) {
43-
Logger.printException(() -> "Failed to parse client token request", ex);
44-
return newResponse(INTERNAL_ERROR);
57+
Logger.printException(() -> "Failed to parse client token request from input stream", ex);
58+
return INTERNAL_ERROR_RESPONSE;
4559
}
4660

47-
try {
48-
ClienttokenHttp.ClientTokenResponse clientTokenResponse =
49-
ClientTokenFetcher.fetchClientToken(clientTokenRequest);
50-
51-
if (clientTokenResponse == null) {
52-
return newResponse(INTERNAL_ERROR);
53-
}
54-
55-
ClienttokenHttp.ClientTokenResponseType responseGranted =
56-
ClienttokenHttp.ClientTokenResponseType.RESPONSE_GRANTED_TOKEN_RESPONSE;
57-
if (clientTokenResponse.getResponseType() == responseGranted) {
58-
Logger.printInfo(() -> "Fetched iOS client token: " +
59-
clientTokenResponse.getGrantedToken().getToken());
60-
}
61-
62-
return newResponse(Response.Status.OK, clientTokenResponse);
63-
} catch (IOException ex) {
64-
Logger.printException(() -> "Failed to get client token response", ex);
61+
ClientTokenResponse response = getClientTokenResponse(clientTokenRequest, RequestListener::requestClientToken);
62+
if (response == null) {
63+
Logger.printException(() -> "Failed to get client token response");
64+
return INTERNAL_ERROR_RESPONSE;
6565
}
66+
67+
return newResponse(Response.Status.OK, response);
68+
}
6669

67-
return newResponse(INTERNAL_ERROR);
70+
@NonNull
71+
private static ClientTokenResponse requestClientToken(ClientTokenRequest request) throws IOException {
72+
HttpURLConnection urlConnection = (HttpURLConnection) new URL(CLIENT_TOKEN_API_URL).openConnection();
73+
urlConnection.setRequestMethod("POST");
74+
urlConnection.setDoOutput(true);
75+
urlConnection.setRequestProperty("Content-Type", "application/x-protobuf");
76+
urlConnection.setRequestProperty("Accept", "application/x-protobuf");
77+
urlConnection.setRequestProperty("User-Agent", IOS_USER_AGENT);
78+
urlConnection.getOutputStream().write(request.toByteArray());
79+
80+
try (InputStream inputStream = urlConnection.getInputStream()) {
81+
return ClientTokenResponse.parseFrom(inputStream);
82+
}
6883
}
6984

7085
@NonNull
71-
private static InputStream limitedInputStream(InputStream inputStream, long contentLength) {
86+
private static InputStream newLimitedInputStream(InputStream inputStream, long contentLength) {
7287
return new FilterInputStream(inputStream) {
7388
private long remaining = contentLength;
7489

@@ -92,18 +107,20 @@ public int read(byte[] b, int off, int len) throws IOException {
92107
}
93108

94109
@NonNull
95-
private static InputStream getRequestBodyInputStream(@NonNull IHTTPSession request) {
96-
long requestContentLength =
97-
Long.parseLong(Objects.requireNonNull(request.getHeaders().get("content-length")));
98-
return limitedInputStream(request.getInputStream(), requestContentLength);
110+
private static InputStream getInputStream(@NonNull IHTTPSession session) {
111+
long requestContentLength = Long.parseLong(Objects.requireNonNull(session.getHeaders().get("content-length")));
112+
return newLimitedInputStream(session.getInputStream(), requestContentLength);
99113
}
100114

115+
private static final Response INTERNAL_ERROR_RESPONSE = newResponse(INTERNAL_ERROR);
116+
101117
@SuppressWarnings("SameParameterValue")
102118
@NonNull
103119
private static Response newResponse(Response.Status status) {
104120
return newResponse(status, null);
105121
}
106122

123+
107124
@NonNull
108125
private static Response newResponse(Response.IStatus status, MessageLite messageLite) {
109126
if (messageLite == null) {

0 commit comments

Comments
 (0)