diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/ScrubbedPreviewLayoutFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/ScrubbedPreviewLayoutFingerprint.kt new file mode 100644 index 000000000..85cc1d0f6 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/ScrubbedPreviewLayoutFingerprint.kt @@ -0,0 +1,28 @@ +package app.revanced.patches.youtube.utils.fix.parameter.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.youtube.utils.resourceid.patch.SharedResourceIdPatch.Companion.Thumbnail +import app.revanced.util.bytecode.isWideLiteralExists +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +object ScrubbedPreviewLayoutFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, + returnType = "V", + parameters = listOf("Landroid/content/Context;", "Landroid/util/AttributeSet;", "I", "I"), + opcodes = listOf( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + Opcode.IPUT_OBJECT, // preview imageview + ), + // This resource is used in ~ 40 different locations, but this method has a distinct list of parameters to match to. + customFingerprint = { methodDef, _ -> methodDef.isWideLiteralExists(Thumbnail) } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailFingerprint.kt new file mode 100644 index 000000000..5c5b3f14c --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailFingerprint.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.youtube.utils.fix.parameter.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +/** + * Resolves using the class found in [StoryboardThumbnailParentFingerprint]. + */ +object StoryboardThumbnailFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "Z", + parameters = emptyList(), + opcodes = listOf( + Opcode.MOVE_RESULT, + Opcode.IF_GTZ, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.RETURN, + Opcode.RETURN, // Last instruction of method. + ), +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailParentFingerprint.kt new file mode 100644 index 000000000..778de31b1 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailParentFingerprint.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.youtube.utils.fix.parameter.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.dexlib2.AccessFlags + +/** + * Here lies code that creates the seekbar thumbnails. + * + * An additional change here might force the thumbnails to be created, + * or possibly a change somewhere else (maybe involving YouTube 18.23.35 class `hte`) + */ +object StoryboardThumbnailParentFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "Landroid/graphics/Bitmap;", + strings = listOf("Storyboard regionDecoder.decodeRegion exception - "), +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/patch/SpoofPlayerParameterPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/patch/SpoofPlayerParameterPatch.kt index 7f3db0b00..92f7a2cc1 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/patch/SpoofPlayerParameterPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/patch/SpoofPlayerParameterPatch.kt @@ -8,6 +8,9 @@ import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.toMethodWalker 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.removeInstruction +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResultSuccess @@ -16,16 +19,23 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patches.youtube.utils.annotations.YouTubeCompatibility import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.BadResponseFingerprint import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.ProtobufParameterBuilderFingerprint +import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.ScrubbedPreviewLayoutFingerprint +import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardThumbnailFingerprint +import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardThumbnailParentFingerprint import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.SubtitleWindowFingerprint import app.revanced.patches.youtube.utils.playertype.patch.PlayerTypeHookPatch +import app.revanced.patches.youtube.utils.resourceid.patch.SharedResourceIdPatch import app.revanced.patches.youtube.utils.settings.resource.patch.SettingsPatch import app.revanced.patches.youtube.utils.videoid.general.patch.VideoIdPatch import app.revanced.util.integrations.Constants.MISC_PATH +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction +import org.jf.dexlib2.iface.instruction.ReferenceInstruction @Name("spoof-player-parameters") @Description("Spoofs player parameters to prevent playback issues.") @DependsOn( [ + SharedResourceIdPatch::class, PlayerTypeHookPatch::class, VideoIdPatch::class ] @@ -36,6 +46,8 @@ class SpoofPlayerParameterPatch : BytecodePatch( listOf( BadResponseFingerprint, ProtobufParameterBuilderFingerprint, + ScrubbedPreviewLayoutFingerprint, + StoryboardThumbnailParentFingerprint, SubtitleWindowFingerprint ) ) { @@ -60,6 +72,51 @@ class SpoofPlayerParameterPatch : BytecodePatch( } } ?: return ProtobufParameterBuilderFingerprint.toErrorResult() + // When the player parameter is spoofed in incognito mode, this value will always be false + // If this value is true, the timestamp and chapter are showned when tapping the seekbar. + StoryboardThumbnailParentFingerprint.result?.classDef?.let { classDef -> + StoryboardThumbnailFingerprint.also { + it.resolve( + context, + classDef + ) + }.result?.let { + it.mutableMethod.apply { + val targetIndex = it.scanResult.patternScanResult!!.endIndex + val targetRegister = getInstruction(targetIndex).registerA + + // Since this is end of the method must replace one line then add the rest. + addInstructions( + targetIndex + 1, + """ + invoke-static {v$targetRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarThumbnailOverrideValue(Z)Z + move-result v$targetRegister + return v$targetRegister + """ + ) + removeInstruction(targetIndex) + } + } ?: return StoryboardThumbnailFingerprint.toErrorResult() + } ?: return StoryboardThumbnailParentFingerprint.toErrorResult() + + // Seekbar thumbnail now show up but are always a blank image. + // Additional changes are needed to force the client to generate the thumbnails (assuming it's possible), + // but for now hide the empty thumbnail. + ScrubbedPreviewLayoutFingerprint.result?.let { + it.mutableMethod.apply { + val endIndex = it.scanResult.patternScanResult!!.endIndex + val imageViewFieldName = getInstruction(endIndex).reference + + addInstructions( + implementation!!.instructions.lastIndex, + """ + iget-object v0, p0, $imageViewFieldName # copy imageview field to a register + invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->seekbarImageViewCreated(Landroid/widget/ImageView;)V + """ + ) + } + } ?: return ScrubbedPreviewLayoutFingerprint.toErrorResult() + // hook video playback result BadResponseFingerprint.result?.mutableMethod?.addInstruction( 0, diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/patch/SharedResourceIdPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/patch/SharedResourceIdPatch.kt index ec3b283e3..efceec08b 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/patch/SharedResourceIdPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/patch/SharedResourceIdPatch.kt @@ -75,6 +75,7 @@ class SharedResourceIdPatch : ResourcePatch { var Scrubbing: Long = -1 var SettingsBooleanTimeRangeDialog: Long = -1 var SuggestedAction: Long = -1 + var Thumbnail: Long = -1 var ToolBarPaddingHome: Long = -1 var ToolTipContentView: Long = -1 var TotalTime: Long = -1 @@ -146,6 +147,7 @@ class SharedResourceIdPatch : ResourcePatch { Scrubbing = find(DIMEN, "vertical_touch_offset_to_enter_fine_scrubbing") SettingsBooleanTimeRangeDialog = find(LAYOUT, "setting_boolean_time_range_dialog") SuggestedAction = find(LAYOUT, "suggested_action") + Thumbnail = find(ID, "thumbnail") ToolBarPaddingHome = find(DIMEN, "toolbar_padding_home_action_up") ToolTipContentView = find(LAYOUT, "tooltip_content_view") TotalTime = find(STRING, "total_time")