use latest patches gradle plugin
This commit is contained in:
parent
1ff148d49f
commit
21e02ed323
9 changed files with 4 additions and 367 deletions
|
|
@ -1,14 +1,7 @@
|
||||||
plugins {
|
|
||||||
alias(libs.plugins.protobuf)
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(project(":extensions:shared:library"))
|
compileOnly(project(":extensions:shared:library"))
|
||||||
compileOnly(project(":extensions:spotify:stub"))
|
compileOnly(project(":extensions:spotify:stub"))
|
||||||
compileOnly(libs.annotation)
|
compileOnly(libs.annotation)
|
||||||
|
|
||||||
implementation(libs.nanohttpd)
|
|
||||||
implementation(libs.protobuf.javalite)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
@ -21,19 +14,3 @@ android {
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protobuf {
|
|
||||||
protoc {
|
|
||||||
artifact = libs.protobuf.protoc.get().toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
generateProtoTasks {
|
|
||||||
all().forEach { task ->
|
|
||||||
task.builtins {
|
|
||||||
create("java") {
|
|
||||||
option("lite")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
package app.revanced.extension.spotify.misc.fix;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import app.revanced.extension.shared.Logger;
|
|
||||||
import app.revanced.extension.spotify.misc.fix.clienttoken.data.v0.ClienttokenHttp.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import static app.revanced.extension.spotify.misc.fix.Constants.*;
|
|
||||||
|
|
||||||
class ClientTokenService {
|
|
||||||
private static final String IOS_CLIENT_ID = "58bd3c95768941ea9eb4350aaa033eb3";
|
|
||||||
private static final String IOS_USER_AGENT;
|
|
||||||
|
|
||||||
static {
|
|
||||||
String clientVersion = getClientVersion();
|
|
||||||
int commitHashIndex = clientVersion.lastIndexOf(".");
|
|
||||||
String version = clientVersion.substring(
|
|
||||||
clientVersion.indexOf("-") + 1,
|
|
||||||
clientVersion.lastIndexOf(".", commitHashIndex - 1)
|
|
||||||
);
|
|
||||||
|
|
||||||
IOS_USER_AGENT = "Spotify/" + version + " iOS/" + getSystemVersion() + " (" + getHardwareMachine() + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final ConnectivitySdkData.Builder IOS_CONNECTIVITY_SDK_DATA =
|
|
||||||
ConnectivitySdkData.newBuilder()
|
|
||||||
.setPlatformSpecificData(PlatformSpecificData.newBuilder()
|
|
||||||
.setIos(NativeIOSData.newBuilder()
|
|
||||||
.setHwMachine(getHardwareMachine())
|
|
||||||
.setSystemVersion(getSystemVersion())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
private static final ClientDataRequest.Builder IOS_CLIENT_DATA_REQUEST =
|
|
||||||
ClientDataRequest.newBuilder()
|
|
||||||
.setClientVersion(getClientVersion())
|
|
||||||
.setClientId(IOS_CLIENT_ID);
|
|
||||||
|
|
||||||
private static final ClientTokenRequest.Builder IOS_CLIENT_TOKEN_REQUEST =
|
|
||||||
ClientTokenRequest.newBuilder()
|
|
||||||
.setRequestType(ClientTokenRequestType.REQUEST_CLIENT_DATA_REQUEST);
|
|
||||||
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
static ClientTokenRequest newIOSClientTokenRequest(String deviceId) {
|
|
||||||
Logger.printInfo(() -> "Creating new iOS client token request with device ID: " + deviceId);
|
|
||||||
|
|
||||||
return IOS_CLIENT_TOKEN_REQUEST
|
|
||||||
.setClientData(IOS_CLIENT_DATA_REQUEST
|
|
||||||
.setConnectivitySdkData(IOS_CONNECTIVITY_SDK_DATA
|
|
||||||
.setDeviceId(deviceId)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
static ClientTokenResponse getClientTokenResponse(@NonNull ClientTokenRequest request) {
|
|
||||||
if (request.getRequestType() == ClientTokenRequestType.REQUEST_CLIENT_DATA_REQUEST) {
|
|
||||||
Logger.printInfo(() -> "Requesting iOS client token");
|
|
||||||
String deviceId = request.getClientData().getConnectivitySdkData().getDeviceId();
|
|
||||||
request = newIOSClientTokenRequest(deviceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientTokenResponse response;
|
|
||||||
try {
|
|
||||||
response = requestClientToken(request);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
Logger.printException(() -> "Failed to handle request", ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static ClientTokenResponse requestClientToken(@NonNull ClientTokenRequest request) throws IOException {
|
|
||||||
HttpURLConnection urlConnection = (HttpURLConnection) new URL(CLIENT_TOKEN_API_URL).openConnection();
|
|
||||||
urlConnection.setRequestMethod("POST");
|
|
||||||
urlConnection.setDoOutput(true);
|
|
||||||
urlConnection.setRequestProperty("Content-Type", "application/x-protobuf");
|
|
||||||
urlConnection.setRequestProperty("Accept", "application/x-protobuf");
|
|
||||||
urlConnection.setRequestProperty("User-Agent", IOS_USER_AGENT);
|
|
||||||
|
|
||||||
byte[] requestArray = request.toByteArray();
|
|
||||||
urlConnection.setFixedLengthStreamingMode(requestArray.length);
|
|
||||||
urlConnection.getOutputStream().write(requestArray);
|
|
||||||
|
|
||||||
try (InputStream inputStream = urlConnection.getInputStream()) {
|
|
||||||
return ClientTokenResponse.parseFrom(inputStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
static ClientTokenResponse serveClientTokenRequest(@NonNull InputStream inputStream) {
|
|
||||||
ClientTokenRequest request;
|
|
||||||
try {
|
|
||||||
request = ClientTokenRequest.parseFrom(inputStream);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
Logger.printException(() -> "Failed to parse request from input stream", ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Logger.printInfo(() -> "Request of type: " + request.getRequestType());
|
|
||||||
|
|
||||||
ClientTokenResponse response = getClientTokenResponse(request);
|
|
||||||
if (response != null) Logger.printInfo(() -> "Response of type: " + response.getResponseType());
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
package app.revanced.extension.spotify.misc.fix;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
class Constants {
|
|
||||||
static final String CLIENT_TOKEN_API_PATH = "/v1/clienttoken";
|
|
||||||
static final String CLIENT_TOKEN_API_URL = "https://clienttoken.spotify.com" + CLIENT_TOKEN_API_PATH;
|
|
||||||
|
|
||||||
// Modified by a patch. Do not touch.
|
|
||||||
@NonNull
|
|
||||||
static String getClientVersion() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modified by a patch. Do not touch.
|
|
||||||
@NonNull
|
|
||||||
static String getSystemVersion() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modified by a patch. Do not touch.
|
|
||||||
@NonNull
|
|
||||||
static String getHardwareMachine() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
package app.revanced.extension.spotify.misc.fix;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import app.revanced.extension.shared.Logger;
|
|
||||||
import app.revanced.extension.spotify.misc.fix.clienttoken.data.v0.ClienttokenHttp.ClientTokenResponse;
|
|
||||||
import com.google.protobuf.MessageLite;
|
|
||||||
import fi.iki.elonen.NanoHTTPD;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.FilterInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static app.revanced.extension.spotify.misc.fix.ClientTokenService.serveClientTokenRequest;
|
|
||||||
import static app.revanced.extension.spotify.misc.fix.Constants.CLIENT_TOKEN_API_PATH;
|
|
||||||
import static fi.iki.elonen.NanoHTTPD.Response.Status.INTERNAL_ERROR;
|
|
||||||
|
|
||||||
class RequestListener extends NanoHTTPD {
|
|
||||||
RequestListener(int port) {
|
|
||||||
super(port);
|
|
||||||
|
|
||||||
try {
|
|
||||||
start();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
Logger.printException(() -> "Failed to start request listener on port " + port, ex);
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Response serve(@NonNull IHTTPSession session) {
|
|
||||||
String uri = session.getUri();
|
|
||||||
if (!uri.equals(CLIENT_TOKEN_API_PATH)) return INTERNAL_ERROR_RESPONSE;
|
|
||||||
|
|
||||||
Logger.printInfo(() -> "Serving request for URI: " + uri);
|
|
||||||
|
|
||||||
ClientTokenResponse response = serveClientTokenRequest(getInputStream(session));
|
|
||||||
if (response != null) return newResponse(Response.Status.OK, response);
|
|
||||||
|
|
||||||
Logger.printException(() -> "Failed to serve client token request");
|
|
||||||
return INTERNAL_ERROR_RESPONSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static InputStream newLimitedInputStream(InputStream inputStream, long contentLength) {
|
|
||||||
return new FilterInputStream(inputStream) {
|
|
||||||
private long remaining = contentLength;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
if (remaining <= 0) return -1;
|
|
||||||
int result = super.read();
|
|
||||||
if (result != -1) remaining--;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(byte[] b, int off, int len) throws IOException {
|
|
||||||
if (remaining <= 0) return -1;
|
|
||||||
len = (int) Math.min(len, remaining);
|
|
||||||
int result = super.read(b, off, len);
|
|
||||||
if (result != -1) remaining -= result;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static InputStream getInputStream(@NonNull IHTTPSession session) {
|
|
||||||
long requestContentLength = Long.parseLong(Objects.requireNonNull(session.getHeaders().get("content-length")));
|
|
||||||
return newLimitedInputStream(session.getInputStream(), requestContentLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Response INTERNAL_ERROR_RESPONSE = newResponse(INTERNAL_ERROR);
|
|
||||||
|
|
||||||
@SuppressWarnings("SameParameterValue")
|
|
||||||
@NonNull
|
|
||||||
private static Response newResponse(Response.Status status) {
|
|
||||||
return newResponse(status, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static Response newResponse(Response.IStatus status, MessageLite messageLite) {
|
|
||||||
if (messageLite == null) {
|
|
||||||
return newFixedLengthResponse(status, "application/x-protobuf", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] messageBytes = messageLite.toByteArray();
|
|
||||||
InputStream stream = new ByteArrayInputStream(messageBytes);
|
|
||||||
return newFixedLengthResponse(status, "application/x-protobuf", stream, messageBytes.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
package app.revanced.extension.spotify.misc.fix;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public class SpoofClientPatch {
|
|
||||||
private static RequestListener listener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point. Launch requests listener server.
|
|
||||||
*/
|
|
||||||
public synchronized static void launchListener(int port) {
|
|
||||||
if (listener != null) {
|
|
||||||
Logger.printInfo(() -> "Listener already running on port " + port);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Logger.printInfo(() -> "Launching listener on port " + port);
|
|
||||||
listener = new RequestListener(port);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "launchListener failure", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package spotify.clienttoken.data.v0;
|
|
||||||
|
|
||||||
option optimize_for = LITE_RUNTIME;
|
|
||||||
option java_package = "app.revanced.extension.spotify.misc.fix.clienttoken.data.v0";
|
|
||||||
|
|
||||||
message ClientTokenRequest {
|
|
||||||
ClientTokenRequestType request_type = 1;
|
|
||||||
|
|
||||||
oneof request {
|
|
||||||
ClientDataRequest client_data = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ClientTokenRequestType {
|
|
||||||
REQUEST_UNKNOWN = 0;
|
|
||||||
REQUEST_CLIENT_DATA_REQUEST = 1;
|
|
||||||
REQUEST_CHALLENGE_ANSWERS_REQUEST = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ClientDataRequest {
|
|
||||||
string client_version = 1;
|
|
||||||
string client_id = 2;
|
|
||||||
|
|
||||||
oneof data {
|
|
||||||
ConnectivitySdkData connectivity_sdk_data = 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message ConnectivitySdkData {
|
|
||||||
PlatformSpecificData platform_specific_data = 1;
|
|
||||||
string device_id = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message PlatformSpecificData {
|
|
||||||
oneof data {
|
|
||||||
NativeIOSData ios = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message NativeIOSData {
|
|
||||||
int32 user_interface_idiom = 1;
|
|
||||||
bool target_iphone_simulator = 2;
|
|
||||||
string hw_machine = 3;
|
|
||||||
string system_version = 4;
|
|
||||||
string simulator_model_identifier = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ClientTokenResponse {
|
|
||||||
ClientTokenResponseType response_type = 1;
|
|
||||||
|
|
||||||
oneof response {
|
|
||||||
GrantedTokenResponse granted_token = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ClientTokenResponseType {
|
|
||||||
RESPONSE_UNKNOWN = 0;
|
|
||||||
RESPONSE_GRANTED_TOKEN_RESPONSE = 1;
|
|
||||||
RESPONSE_CHALLENGES_RESPONSE = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GrantedTokenResponse {
|
|
||||||
string token = 1;
|
|
||||||
int32 expires_after_seconds = 2;
|
|
||||||
int32 refresh_after_seconds = 3;
|
|
||||||
repeated TokenDomain domains = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message TokenDomain {
|
|
||||||
string domain = 1;
|
|
||||||
}
|
|
||||||
|
|
@ -2,5 +2,6 @@ org.gradle.caching = true
|
||||||
org.gradle.jvmargs = -Xms512M -Xmx2048M
|
org.gradle.jvmargs = -Xms512M -Xmx2048M
|
||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
android.useAndroidX = true
|
android.useAndroidX = true
|
||||||
|
android.uniquePackageNames=false
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 5.50.0-dev.4
|
version = 5.50.0-dev.4
|
||||||
|
|
|
||||||
|
|
@ -11,25 +11,15 @@ appcompat = "1.7.1"
|
||||||
okhttp = "5.3.2"
|
okhttp = "5.3.2"
|
||||||
retrofit = "3.0.0"
|
retrofit = "3.0.0"
|
||||||
guava = "33.5.0-jre"
|
guava = "33.5.0-jre"
|
||||||
protobuf-javalite = "4.33.2"
|
|
||||||
protoc = "4.33.2"
|
|
||||||
protobuf = "0.9.6"
|
|
||||||
antlr4 = "4.13.2"
|
|
||||||
nanohttpd = "2.3.1"
|
|
||||||
apksig = "8.12.3"
|
apksig = "8.12.3"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
|
annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
|
||||||
antlr4 = { module = "org.antlr:antlr4", version.ref = "antlr4" }
|
|
||||||
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||||
nanohttpd = { module = "org.nanohttpd:nanohttpd", version.ref = "nanohttpd" }
|
|
||||||
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||||
protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protobuf-javalite" }
|
|
||||||
protobuf-protoc = { module = "com.google.protobuf:protoc", version.ref = "protoc" }
|
|
||||||
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||||
apksig = { group = "com.android.tools.build", name = "apksig", version.ref = "apksig" }
|
apksig = { group = "com.android.tools.build", name = "apksig", version.ref = "apksig" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-library = { id = "com.android.library" }
|
android-library = { id = "com.android.library" }
|
||||||
protobuf = { id = "com.google.protobuf", version.ref = "protobuf" }
|
|
||||||
|
|
|
||||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,5 +1,7 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue