Compare commits

...

5 Commits

Author SHA1 Message Date
semantic-release-bot
4c44982cde chore: Release v5.5.0-dev.3 [skip ci]
# [5.5.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.2...v5.5.0-dev.3) (2024-12-16)

### Features

* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([a7aab9a](a7aab9aeca))
2024-12-16 18:18:23 +00:00
ILoveOpenSourceApplications
a7aab9aeca feat(YouTube - Hide feed components): Remove obsolete Hide search result shelf header option (#4134) 2024-12-16 19:15:22 +01:00
semantic-release-bot
7a8486f562 chore: Release v5.5.0-dev.2 [skip ci]
# [5.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.1...v5.5.0-dev.2) (2024-12-16)

### Bug Fixes

* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([ccb6a7f](ccb6a7f161))
2024-12-16 18:10:37 +00:00
oSumAtrIX
ccb6a7f161 fix(YouTube Music): Add Spoof client patch to fix playback (#4132) 2024-12-16 19:07:37 +01:00
LisoUseInAIKyrios
c792edfb77 chore: fix typo 2024-12-15 17:06:36 +04:00
18 changed files with 290 additions and 106 deletions

View File

@@ -1,3 +1,17 @@
# [5.5.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.2...v5.5.0-dev.3) (2024-12-16)
### Features
* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([c71443a](https://github.com/ReVanced/revanced-patches/commit/c71443a08883ab10ef2553213c03b00e7c580a43))
# [5.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.1...v5.5.0-dev.2) (2024-12-16)
### Bug Fixes
* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([b092508](https://github.com/ReVanced/revanced-patches/commit/b0925088e8b41636e285cb234593d545604ce461))
# [5.5.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.1-dev.1...v5.5.0-dev.1) (2024-12-15)

View File

@@ -0,0 +1 @@
// Do not remove. Necessary for the extension plugin to be applied to the project.

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,27 @@
package app.revanced.extension.music.spoof;
/**
* @noinspection unused
*/
public class SpoofClientPatch {
private static final int CLIENT_TYPE_ID = 26;
private static final String CLIENT_VERSION = "6.21";
private static final String DEVICE_MODEL = "iPhone16,2";
private static final String OS_VERSION = "17.7.2.21H221";
public static int getClientId() {
return CLIENT_TYPE_ID;
}
public static String getClientVersion() {
return CLIENT_VERSION;
}
public static String getClientModel() {
return DEVICE_MODEL;
}
public static String getOsVersion() {
return OS_VERSION;
}
}

View File

@@ -36,7 +36,6 @@ public final class LayoutComponentsFilter extends Filter {
);
private final StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup searchResultShelfHeader;
private final StringFilterGroup inFeedSurvey;
private final StringFilterGroup notifyMe;
private final StringFilterGroup expandableMetadata;
@@ -194,11 +193,6 @@ public final class LayoutComponentsFilter extends Filter {
"timed_reaction"
);
searchResultShelfHeader = new StringFilterGroup(
Settings.HIDE_SEARCH_RESULT_SHELF_HEADER,
"shelf_header.eml"
);
notifyMe = new StringFilterGroup(
Settings.HIDE_NOTIFY_ME_BUTTON,
"set_reminder_button"
@@ -324,9 +318,6 @@ public final class LayoutComponentsFilter extends Filter {
return false;
}
// TODO: This also hides the feed Shorts shelf header
if (matchedGroup == searchResultShelfHeader && contentIndex != 0) return false;
if (matchedGroup == horizontalShelves) {
if (contentIndex == 0 && hideShelves()) {
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);

View File

@@ -93,7 +93,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_NOTIFY_ME_BUTTON = new BooleanSetting("revanced_hide_notify_me_button", TRUE);
public static final BooleanSetting HIDE_PLAYABLES = new BooleanSetting("revanced_hide_playables", TRUE);
public static final BooleanSetting HIDE_SEARCH_RESULT_RECOMMENDATIONS = new BooleanSetting("revanced_hide_search_result_recommendations", TRUE);
public static final BooleanSetting HIDE_SEARCH_RESULT_SHELF_HEADER = new BooleanSetting("revanced_hide_search_result_shelf_header", FALSE);
public static final BooleanSetting HIDE_SHOW_MORE_BUTTON = new BooleanSetting("revanced_hide_show_more_button", TRUE, true);
// Alternative thumbnails
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_home", ThumbnailOption.ORIGINAL);

View File

@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
org.gradle.parallel = true
android.useAndroidX = true
kotlin.code.style = official
version = 5.5.0-dev.1
version = 5.5.0-dev.3

View File

@@ -324,8 +324,12 @@ public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatchKt {
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatchKt {
public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public final class app/revanced/patches/music/misc/spoof/SpoofClientPatchKt {
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/misc/spoof/UserAgentClientSpoofPatchKt {
public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt {
@@ -766,6 +770,10 @@ public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch
public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt {
public static final fun userAgentClientSpoofPatch (Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatchKt {
public static final fun getRemoveFileSizeLimitPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}

View File

@@ -4,5 +4,6 @@ import app.revanced.patches.music.misc.extension.hooks.applicationInitHook
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
val sharedExtensionPatch = sharedExtensionPatch(
"music",
applicationInitHook,
)

View File

@@ -4,7 +4,7 @@ import app.revanced.patcher.patch.Option
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.spoof.spoofVideoStreamsPatch
import app.revanced.patches.music.misc.spoof.spoofClientPatch
import app.revanced.patches.shared.castContextFetchFingerprint
import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
import app.revanced.patches.shared.primeMethodFingerprint
@@ -21,7 +21,7 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
extensionPatch = sharedExtensionPatch,
gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch,
) {
dependsOn(spoofVideoStreamsPatch)
dependsOn(spoofClientPatch)
compatibleWith(MUSIC_PACKAGE_NAME)
}

View File

@@ -0,0 +1,39 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val playerRequestConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
strings("player")
}
/**
* Matches using the class found in [playerRequestConstructorFingerprint].
*/
internal val createPlayerRequestBodyFingerprint = fingerprint {
parameters("L")
returns("V")
opcodes(
Opcode.CHECK_CAST,
Opcode.IGET,
Opcode.AND_INT_LIT16,
)
strings("ms")
}
/**
* Used to get a reference to other clientInfo fields.
*/
internal val setClientInfoFieldsFingerprint = fingerprint {
returns("L")
strings("Google Inc.")
}
/**
* Used to get a reference to the clientInfo and clientInfo.clientVersion field.
*/
internal val setClientInfoClientVersionFingerprint = fingerprint {
strings("10.29")
}

View File

@@ -0,0 +1,105 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/music/spoof/SpoofClientPatch;"
// TODO: Replace this patch with spoofVideoStreamsPatch once possible.
val spoofClientPatch = bytecodePatch(
name = "Spoof client",
description = "Spoofs the client to fix playback.",
) {
compatibleWith("com.google.android.apps.youtube.music")
dependsOn(
sharedExtensionPatch,
// TODO: Add settingsPatch
userAgentClientSpoofPatch,
)
execute {
val playerRequestClass = playerRequestConstructorFingerprint.classDef
val createPlayerRequestBodyMatch = createPlayerRequestBodyFingerprint.match(playerRequestClass)
val clientInfoContainerClass = createPlayerRequestBodyMatch.method
.getInstruction(createPlayerRequestBodyMatch.patternMatch!!.startIndex)
.getReference<TypeReference>()!!.type
val clientInfoField = setClientInfoClientVersionFingerprint.method.instructions.first {
it.opcode == Opcode.IPUT_OBJECT && it.getReference<FieldReference>()!!.definingClass == clientInfoContainerClass
}.getReference<FieldReference>()!!
val setClientInfoFieldInstructions = setClientInfoFieldsFingerprint.method.instructions.filter {
(it.opcode == Opcode.IPUT_OBJECT || it.opcode == Opcode.IPUT) &&
it.getReference<FieldReference>()!!.definingClass == clientInfoField.type
}.map { it.getReference<FieldReference>()!! }
// Offsets are known for the fields in the clientInfo object.
val clientIdField = setClientInfoFieldInstructions[0]
val clientModelField = setClientInfoFieldInstructions[5]
val osVersionField = setClientInfoFieldInstructions[7]
val clientVersionField = setClientInfoClientVersionFingerprint.method
.getInstruction(setClientInfoClientVersionFingerprint.stringMatches!!.first().index + 1)
.getReference<FieldReference>()
// Helper method to spoof the client info.
val spoofClientInfoMethod = ImmutableMethod(
playerRequestClass.type,
"spoofClientInfo",
listOf(ImmutableMethodParameter(clientInfoContainerClass, null, null)),
"V",
AccessFlags.PRIVATE.value or AccessFlags.STATIC.value,
null,
null,
MutableMethodImplementation(3),
).toMutable().also(playerRequestClass.methods::add).apply {
addInstructions(
"""
iget-object v0, p0, $clientInfoField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientId()I
move-result v1
iput v1, v0, $clientIdField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientModel()Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $clientModelField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientVersion()Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $clientVersionField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getOsVersion()Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $osVersionField
return-void
""",
)
}
createPlayerRequestBodyMatch.method.apply {
val checkCastIndex = createPlayerRequestBodyMatch.patternMatch!!.startIndex
val clientInfoContainerRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
addInstruction(checkCastIndex + 1, "invoke-static {v$clientInfoContainerRegister}, $spoofClientInfoMethod")
}
}
}

View File

@@ -1,7 +0,0 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
compatibleWith("com.google.android.apps.youtube.music")
})

View File

@@ -0,0 +1,5 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patches.shared.misc.spoof.userAgentClientSpoofPatch
val userAgentClientSpoofPatch = userAgentClientSpoofPatch("com.google.android.apps.youtube.music")

View File

@@ -0,0 +1,81 @@
package app.revanced.patches.shared.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE =
"Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;"
fun userAgentClientSpoofPatch(originalPackageName: String) = transformInstructionsPatch(
filterMap = { classDef, _, instruction, instructionIndex ->
filterMapInstruction35c<MethodCall>(
"Lapp/revanced/extension",
classDef,
instruction,
instructionIndex,
)
},
transform = transform@{ mutableMethod, entry ->
val (_, _, instructionIndex) = entry
// Replace the result of context.getPackageName(), if it is used in a user agent string.
mutableMethod.apply {
// After context.getPackageName() the result is moved to a register.
val targetRegister = (
getInstruction(instructionIndex + 1)
as? OneRegisterInstruction ?: return@transform
).registerA
// IndexOutOfBoundsException is technically possible here,
// but no such occurrences are present in the app.
val referee = getInstruction(instructionIndex + 2).getReference<MethodReference>()?.toString()
// Only replace string builder usage.
if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) {
return@transform
}
// Do not change the package name in methods that use resources, or for methods that use GmsCore.
// Changing these package names will result in playback limitations,
// particularly Android VR background audio only playback.
val resourceOrGmsStringInstructionIndex = indexOfFirstInstruction {
val reference = getReference<StringReference>()
opcode == Opcode.CONST_STRING &&
(reference?.string == "android.resource://" || reference?.string == "gcore_")
}
if (resourceOrGmsStringInstructionIndex >= 0) {
return@transform
}
// Overwrite the result of context.getPackageName() with the original package name.
replaceInstruction(
instructionIndex + 1,
"const-string v$targetRegister, \"$originalPackageName\"",
)
}
},
)
@Suppress("unused")
private enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,
override val returnType: String,
) : IMethodCall {
GetPackageName(
"Landroid/content/Context;",
"getPackageName",
emptyArray(),
"Ljava/lang/String;",
),
}

View File

@@ -222,7 +222,6 @@ val hideLayoutComponentsPatch = bytecodePatch(
SwitchPreference("revanced_hide_notify_me_button"),
SwitchPreference("revanced_hide_playables"),
SwitchPreference("revanced_hide_search_result_recommendations"),
SwitchPreference("revanced_hide_search_result_shelf_header"),
SwitchPreference("revanced_hide_show_more_button"),
SwitchPreference("revanced_hide_doodles"),
)

View File

@@ -1,82 +1,5 @@
package app.revanced.patches.youtube.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
import app.revanced.patches.shared.misc.spoof.userAgentClientSpoofPatch
private const val ORIGINAL_PACKAGE_NAME = "com.google.android.youtube"
private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE =
"Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;"
val userAgentClientSpoofPatch = transformInstructionsPatch(
filterMap = { classDef, _, instruction, instructionIndex ->
filterMapInstruction35c<MethodCall>(
"Lapp/revanced/extension",
classDef,
instruction,
instructionIndex,
)
},
transform = transform@{ mutableMethod, entry ->
val (_, _, instructionIndex) = entry
// Replace the result of context.getPackageName(), if it is used in a user agent string.
mutableMethod.apply {
// After context.getPackageName() the result is moved to a register.
val targetRegister = (
getInstruction(instructionIndex + 1)
as? OneRegisterInstruction ?: return@transform
).registerA
// IndexOutOfBoundsException is technically possible here,
// but no such occurrences are present in the app.
val referee = getInstruction(instructionIndex + 2).getReference<MethodReference>()?.toString()
// Only replace string builder usage.
if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) {
return@transform
}
// Do not change the package name in methods that use resources, or for methods that use GmsCore.
// Changing these package names will result in playback limitations,
// particularly Android VR background audio only playback.
val resourceOrGmsStringInstructionIndex = indexOfFirstInstruction {
val reference = getReference<StringReference>()
opcode == Opcode.CONST_STRING &&
(reference?.string == "android.resource://" || reference?.string == "gcore_")
}
if (resourceOrGmsStringInstructionIndex >= 0) {
return@transform
}
// Overwrite the result of context.getPackageName() with the original package name.
replaceInstruction(
instructionIndex + 1,
"const-string v$targetRegister, \"$ORIGINAL_PACKAGE_NAME\"",
)
}
},
)
@Suppress("unused")
private enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,
override val returnType: String,
) : IMethodCall {
GetPackageName(
"Landroid/content/Context;",
"getPackageName",
emptyArray(),
"Ljava/lang/String;",
),
}
val userAgentClientSpoofPatch = userAgentClientSpoofPatch("com.google.android.youtube")

View File

@@ -158,9 +158,6 @@ You will not be notified of any unexpected events."</string>
<string name="revanced_hide_timed_reactions_title">Hide timed reactions</string>
<string name="revanced_hide_timed_reactions_summary_on">Timed reactions are hidden</string>
<string name="revanced_hide_timed_reactions_summary_off">Timed reactions are shown</string>
<string name="revanced_hide_search_result_shelf_header_title">Hide search result shelf header</string>
<string name="revanced_hide_search_result_shelf_header_summary_on">Shelf header is hidden</string>
<string name="revanced_hide_search_result_shelf_header_summary_off">Shelf header is shown</string>
<string name="revanced_hide_channel_guidelines_title">Hide channel guidelines</string>
<string name="revanced_hide_channel_guidelines_summary_on">Channel guidelines are hidden</string>
<string name="revanced_hide_channel_guidelines_summary_off">Channel guidelines are shown</string>
@@ -1227,7 +1224,7 @@ Enabling this can unlock higher video qualities"</string>
<string name="revanced_disable_zoom_haptics_summary_off">Haptics are enabled</string>
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<string name="revanced_force_original_audio_title">Forced original audio</string>
<string name="revanced_force_original_audio_title">Force original audio</string>
<string name="revanced_force_original_audio_summary_on">Using original audio</string>
<string name="revanced_force_original_audio_summary_off">Using default audio</string>
</patch>