Skip to content

Commit dfd7461

Browse files
committed
feat(Spotify): Add Sanitize sharing links patch
1 parent 2629b52 commit dfd7461

File tree

3 files changed

+92
-4
lines changed

3 files changed

+92
-4
lines changed
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;
2+
3+
@SuppressWarnings("unused")
4+
public class UrlCleaner {
5+
/**
6+
* Removes the "?si=" parameter and anything after it from a URL string.
7+
*
8+
* @param url The original URL string.
9+
* @return The URL string with the "?si=" parameter removed, or the original URL if not found or if input is null.
10+
*/
11+
public static String removeSiParameter(String url) {
12+
if (url == null) {
13+
return null;
14+
}
15+
int siIndex = url.indexOf("?si=");
16+
if (siIndex >= 0) {
17+
// Found "?si=", return the substring before it
18+
return url.substring(0, siIndex);
19+
} else {
20+
// "?si=" not found, return the original URL
21+
return url;
22+
}
23+
}
24+
}

patches/src/main/kotlin/app/revanced/patches/spotify/extended/RemoveCreateTabPatch.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,22 @@ package app.revanced.patches.spotify.extended
33
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
44
import app.revanced.patcher.fingerprint
55
import app.revanced.patcher.patch.bytecodePatch
6-
import app.revanced.patches.shared.mapping.getResourceId
76
import app.revanced.patches.shared.mapping.ResourceType.STRING
7+
import app.revanced.patches.shared.mapping.getResourceId
88
import app.revanced.patches.shared.mapping.resourceMappingPatch
99
import app.revanced.util.containsLiteralInstruction
1010

1111
internal val addCreateTabMethodFingerprint = fingerprint {
1212
returns("V")
13-
custom { method, _ ->
13+
custom { method, _ ->
1414
method.containsLiteralInstruction(getResourceId(STRING, "bottom_navigation_bar_create_tab_title"))
15-
}
15+
}
1616
}
1717

1818
@Suppress("unused")
1919
val removeCreateTabPatch = bytecodePatch(
2020
name = "Remove Create tab",
2121
description = "Removes the 'Create' (Plus) tab from the bottom navigation bar.",
22-
false,
2322
) {
2423
compatibleWith("com.spotify.music")
2524
dependsOn(resourceMappingPatch)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package app.revanced.patches.spotify.extended
2+
3+
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
4+
import app.revanced.patcher.extensions.InstructionExtensions.instructionsOrNull
5+
import app.revanced.patcher.fingerprint
6+
import app.revanced.patcher.patch.bytecodePatch
7+
import app.revanced.util.getReference
8+
import app.revanced.util.indexOfFirstInstructionOrThrow
9+
import com.android.tools.smali.dexlib2.AccessFlags
10+
import com.android.tools.smali.dexlib2.Opcode
11+
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
12+
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
13+
import com.android.tools.smali.dexlib2.iface.reference.StringReference
14+
15+
val shareLinkFingerprint = fingerprint {
16+
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
17+
parameters(
18+
"Ljava/lang/String;",
19+
"Ljava/lang/String;",
20+
"Ljava/lang/String;",
21+
"Ljava/lang/String;"
22+
)
23+
returns("V")
24+
25+
custom { _, classDef ->
26+
val toStringMethod = classDef.methods.firstOrNull {
27+
it.name == "toString" && it.parameters.isEmpty() && it.returnType == "Ljava/lang/String;"
28+
} ?: return@custom false
29+
30+
val toStringInstructions = toStringMethod.instructionsOrNull ?: return@custom false
31+
toStringInstructions.any { instruction ->
32+
instruction.opcode == Opcode.CONST_STRING &&
33+
(instruction as? ReferenceInstruction)?.reference?.let { ref ->
34+
(ref as? StringReference)?.string?.startsWith("ShareUrl(url=") == true
35+
} == true
36+
}
37+
}
38+
}
39+
40+
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/misc/UrlCleaner;"
41+
42+
@Suppress("unused")
43+
val removeShareTrackingPatch = bytecodePatch(
44+
name = "Sanitize sharing links",
45+
description = "Removes the '?si=' tracking parameter from shared links (e.g., Copy Link, Share to...).",
46+
) {
47+
compatibleWith("com.spotify.music")
48+
49+
execute {
50+
val originalMethod = shareLinkFingerprint.method
51+
val invokeDirectIndex = originalMethod.indexOfFirstInstructionOrThrow {
52+
opcode == Opcode.INVOKE_DIRECT &&
53+
(this as ReferenceInstruction).reference is MethodReference &&
54+
(getReference<MethodReference>()?.name == "<init>") &&
55+
(getReference<MethodReference>()?.definingClass == "Ljava/lang/Object;")
56+
}
57+
58+
val smaliCodeToInsert = """
59+
invoke-static {p1}, $EXTENSION_CLASS_DESCRIPTOR->removeSiParameter(Ljava/lang/String;)Ljava/lang/String;
60+
move-result-object p1
61+
""".trimIndent()
62+
63+
originalMethod.addInstructions(invokeDirectIndex + 1, smaliCodeToInsert)
64+
}
65+
}

0 commit comments

Comments
 (0)