fix(Spotify - Unlock Premium): Override additional attributes (#4651)

Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
This commit is contained in:
xC3FFF0E 2025-03-28 18:52:23 +08:00 committed by GitHub
parent 5f4c42bfa9
commit 568b40da96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 82 additions and 26 deletions

View File

@ -1,3 +1,16 @@
dependencies { dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:spotify:stub")) compileOnly(project(":extensions:spotify:stub"))
compileOnly(libs.annotation)
}
android {
defaultConfig {
minSdk = 24
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
} }

View File

@ -2,38 +2,57 @@ package app.revanced.extension.spotify.misc;
import com.spotify.remoteconfig.internal.AccountAttribute; import com.spotify.remoteconfig.internal.AccountAttribute;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
/** /**
* @noinspection unused * @noinspection unused
*/ */
public final class UnlockPremiumPatch { public final class UnlockPremiumPatch {
private static final Map<String, Object> OVERRIDES = Map.of(
// Disables player and app ads.
"ads", false,
// Works along on-demand, allows playing any song without restriction.
"player-license", "premium",
// Disables shuffle being initially enabled when first playing a playlist.
"shuffle", false,
// Allows playing any song on-demand, without a shuffled order.
"on-demand", true,
// Make sure playing songs is not disabled remotely and playlists show up.
"streaming", true,
// Allows adding songs to queue and removes the smart shuffle mode restriction,
// allowing to pick any of the other modes.
"pick-and-shuffle", false,
// Disables shuffle-mode streaming-rule, which forces songs to be played shuffled
// and breaks the player when other patches are applied.
"streaming-rules", "",
// Enables premium UI in settings and removes the premium button in the nav-bar.
"nft-disabled", "1"
);
private static final Map<String, Object> OVERRIDES = new HashMap<>() {{
// Disables player and app ads.
put("ads", false);
// Works along on-demand, allows playing any song without restriction.
put("player-license", "premium");
// Disables shuffle being initially enabled when first playing a playlist.
put("shuffle", false);
// Allows playing any song on-demand, without a shuffled order.
put("on-demand", true);
// Make sure playing songs is not disabled remotely and playlists show up.
put("streaming", true);
// Allows adding songs to queue and removes the smart shuffle mode restriction,
// allowing to pick any of the other modes.
put("pick-and-shuffle", false);
// Disables shuffle-mode streaming-rule, which forces songs to be played shuffled
// and breaks the player when other patches are applied.
put("streaming-rules", "");
// Enables premium UI in settings and removes the premium button in the nav-bar.
put("nft-disabled", "1");
// Enable Cross-Platform Spotify Car Thing.
put("can_use_superbird", true);
// Removes the premium button in the nav-bar for tablet users.
put("tablet-free", false);
}};
/**
* Injection point.
*/
public static void overrideAttribute(Map<String, AccountAttribute> attributes) { public static void overrideAttribute(Map<String, AccountAttribute> attributes) {
for (var entry : OVERRIDES.entrySet()) { try {
var attribute = Objects.requireNonNull(attributes.get(entry.getKey())); for (var entry : OVERRIDES.entrySet()) {
attribute.value_ = entry.getValue(); var key = entry.getKey();
var attribute = attributes.get(key);
if (attribute == null) {
Logger.printException(() -> "Account attribute not found: " + key);
} else {
attribute.value_ = entry.getValue();
}
}
} catch (Exception ex) {
Logger.printException(() -> "overrideAttribute failure", ex);
} }
} }
} }

View File

@ -820,6 +820,10 @@ public final class app/revanced/patches/spotify/misc/UnlockPremiumPatchKt {
public static final fun getUnlockPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getUnlockPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/spotify/misc/extension/ExtensionPatchKt {
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt { public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt {
public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }

View File

@ -10,6 +10,7 @@ import org.w3c.dom.Element
val customThemePatch = resourcePatch( val customThemePatch = resourcePatch(
name = "Custom theme", name = "Custom theme",
description = "Applies a custom theme.", description = "Applies a custom theme.",
use = false,
) { ) {
compatibleWith("com.spotify.music") compatibleWith("com.spotify.music")

View File

@ -4,9 +4,12 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/misc/UnlockPremiumPatch;"
@Suppress("unused") @Suppress("unused")
val unlockPremiumPatch = bytecodePatch( val unlockPremiumPatch = bytecodePatch(
name = "Unlock Spotify Premium", name = "Unlock Spotify Premium",
@ -14,11 +17,12 @@ val unlockPremiumPatch = bytecodePatch(
) { ) {
compatibleWith("com.spotify.music") compatibleWith("com.spotify.music")
extendWith("extensions/spotify.rve") dependsOn(sharedExtensionPatch)
execute { execute {
// Make _value accessible so that it can be overridden in the extension. // Make _value accessible so that it can be overridden in the extension.
accountAttributeFingerprint.classDef.fields.first { it.name == "value_" }.apply { accountAttributeFingerprint.classDef.fields.first { it.name == "value_" }.apply {
// Add public flag and remove private.
accessFlags = accessFlags.or(AccessFlags.PUBLIC.value).and(AccessFlags.PRIVATE.value.inv()) accessFlags = accessFlags.or(AccessFlags.PUBLIC.value).and(AccessFlags.PRIVATE.value.inv())
} }
@ -28,7 +32,7 @@ val unlockPremiumPatch = bytecodePatch(
productStateProtoFingerprint.method.addInstruction( productStateProtoFingerprint.method.addInstruction(
instantiateUnmodifiableMapIndex, instantiateUnmodifiableMapIndex,
"invoke-static { v$attributesMapRegister }," + "invoke-static { v$attributesMapRegister }," +
"Lapp/revanced/extension/spotify/misc/UnlockPremiumPatch;->overrideAttribute(Ljava/util/Map;)V", "$EXTENSION_CLASS_DESCRIPTOR->overrideAttribute(Ljava/util/Map;)V",
) )
// Add the query parameter trackRows to show popular tracks in the artist page. // Add the query parameter trackRows to show popular tracks in the artist page.

View File

@ -0,0 +1,5 @@
package app.revanced.patches.spotify.misc.extension
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
val sharedExtensionPatch = sharedExtensionPatch("spotify", spotifyMainActivityOnCreate)

View File

@ -0,0 +1,10 @@
package app.revanced.patches.spotify.misc.extension
import app.revanced.patches.shared.misc.extension.extensionHook
internal val spotifyMainActivityOnCreate = extensionHook {
custom { method, classDef ->
classDef.type == "Lcom/spotify/music/SpotifyMainActivity;" &&
method.name == "onCreate"
}
}