Skip to content

Commit

Permalink
feat(music): add sponsorblock patch inotia00/ReVanced_Extended#97
Browse files Browse the repository at this point in the history
  • Loading branch information
inotia00 authored and YT-Advanced committed Sep 14, 2023
1 parent f9da11d commit 88d6389
Show file tree
Hide file tree
Showing 15 changed files with 601 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package app.revanced.patches.music.utils.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.music.utils.resourceid.patch.SharedResourceIdPatch.Companion.InlineTimeBarAdBreakMarkerColor
import app.revanced.util.bytecode.isWideLiteralExists

object SeekBarConstructorFingerprint : MethodFingerprint(
returnType = "V",
customFingerprint = { methodDef, _ -> methodDef.isWideLiteralExists(InlineTimeBarAdBreakMarkerColor) }
)
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class SharedResourceIdPatch : ResourcePatch {
var ColorGrey: Long = -1
var DialogSolid: Long = -1
var DisabledIconAlpha: Long = -1
var InlineTimeBarAdBreakMarkerColor: Long = -1
var IsTablet: Long = -1
var MusicMenuLikeButtons: Long = -1
var MusicNotifierShelf: Long = -1
Expand All @@ -44,6 +45,7 @@ class SharedResourceIdPatch : ResourcePatch {
ColorGrey = find(COLOR, "ytm_color_grey_12")
DialogSolid = find(STYLE, "Theme.YouTubeMusic.Dialog.Solid")
DisabledIconAlpha = find(DIMEN, "disabled_icon_alpha")
InlineTimeBarAdBreakMarkerColor = find(COLOR, "inline_time_bar_ad_break_marker_color")
IsTablet = find(BOOL, "is_tablet")
MusicMenuLikeButtons = find(LAYOUT, "music_menu_like_buttons")
MusicNotifierShelf = find(LAYOUT, "music_notifier_shelf")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package app.revanced.patches.music.utils.sponsorblock.bytecode.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

object MusicPlaybackControlsTimeBarDrawFingerprint : MethodFingerprint(
returnType = "V",
customFingerprint = { methodDef, _ ->
methodDef.definingClass.endsWith("/MusicPlaybackControlsTimeBar;")
&& methodDef.name == "draw"
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package app.revanced.patches.music.utils.sponsorblock.bytecode.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode

object MusicPlaybackControlsTimeBarOnMeasureFingerprint : MethodFingerprint(
returnType = "V",
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.INVOKE_VIRTUAL,
Opcode.RETURN_VOID
),
customFingerprint = { methodDef, _ ->
methodDef.definingClass.endsWith("/MusicPlaybackControlsTimeBar;")
&& methodDef.name == "onMeasure"
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package app.revanced.patches.music.utils.sponsorblock.bytecode.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

object SeekbarOnDrawFingerprint : MethodFingerprint(
customFingerprint = { methodDef, _ -> methodDef.name == "onDraw" }
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package app.revanced.patches.music.utils.sponsorblock.bytecode.patch

import app.revanced.extensions.exception
import app.revanced.patcher.data.BytecodeContext
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.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patches.music.utils.fingerprints.SeekBarConstructorFingerprint
import app.revanced.patches.music.utils.resourceid.patch.SharedResourceIdPatch
import app.revanced.patches.music.utils.sponsorblock.bytecode.fingerprints.MusicPlaybackControlsTimeBarDrawFingerprint
import app.revanced.patches.music.utils.sponsorblock.bytecode.fingerprints.MusicPlaybackControlsTimeBarOnMeasureFingerprint
import app.revanced.patches.music.utils.sponsorblock.bytecode.fingerprints.SeekbarOnDrawFingerprint
import app.revanced.patches.music.utils.videoid.patch.VideoIdPatch
import app.revanced.patches.music.utils.videoinformation.patch.VideoInformationPatch
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction3rc
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference

@DependsOn(
[
SharedResourceIdPatch::class,
VideoIdPatch::class,
VideoInformationPatch::class
]
)
class SponsorBlockBytecodePatch : BytecodePatch(
listOf(
MusicPlaybackControlsTimeBarDrawFingerprint,
MusicPlaybackControlsTimeBarOnMeasureFingerprint,
SeekBarConstructorFingerprint
)
) {
override fun execute(context: BytecodeContext) {

/**
* Hook the video time methods & Initialize the player controller
*/
VideoInformationPatch.apply {
videoTimeHook(
INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR,
"setVideoTime"
)
onCreateHook(
INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR,
"initialize"
)
}


/**
* Responsible for seekbar in fullscreen
*/
SeekBarConstructorFingerprint.result?.classDef?.let { classDef ->
SeekbarOnDrawFingerprint.also {
it.resolve(
context,
classDef
)
}.result?.let {
it.mutableMethod.apply {
// Initialize seekbar method
addInstructions(
0, """
move-object/from16 v0, p0
const-string v1, "${VideoInformationPatch.rectangleFieldName}"
invoke-static {v0, v1}, $INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarRect(Ljava/lang/Object;Ljava/lang/String;)V
"""
)

// Set seekbar thickness
for ((index, instruction) in implementation!!.instructions.withIndex()) {
if (instruction.opcode != Opcode.INVOKE_STATIC) continue

val invokeInstruction = getInstruction<Instruction35c>(index)
if ((invokeInstruction.reference as MethodReference).name != "round") continue

val insertIndex = index + 2

addInstruction(
insertIndex,
"invoke-static {v${invokeInstruction.registerC}}, $INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarThickness(I)V"
)
break
}

// Draw segment
for ((index, instruction) in implementation!!.instructions.withIndex()) {
if (instruction.opcode != Opcode.INVOKE_VIRTUAL_RANGE) continue

val invokeInstruction = instruction as BuilderInstruction3rc
if ((invokeInstruction.reference as MethodReference).name != "restore") continue

val drawSegmentInstructionInsertIndex = index - 1

val (canvasInstance, centerY) =
getInstruction<FiveRegisterInstruction>(
drawSegmentInstructionInsertIndex
).let { drawSegmentInstruction ->
drawSegmentInstruction.registerC to drawSegmentInstruction.registerE
}

addInstruction(
drawSegmentInstructionInsertIndex,
"invoke-static {v$canvasInstance, v$centerY}, $INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->drawSponsorTimeBars(Landroid/graphics/Canvas;F)V"
)
break
}
}
} ?: throw SeekbarOnDrawFingerprint.exception
} ?: throw SeekBarConstructorFingerprint.exception


/**
* Responsible for seekbar in player
*/
MusicPlaybackControlsTimeBarOnMeasureFingerprint.result?.let {
it.mutableMethod.apply {
val rectangleIndex = it.scanResult.patternScanResult!!.startIndex
val rectangleReference = getInstruction<ReferenceInstruction>(rectangleIndex).reference
rectangleFieldName = (rectangleReference as FieldReference).name
}
} ?: throw MusicPlaybackControlsTimeBarOnMeasureFingerprint.exception

MusicPlaybackControlsTimeBarDrawFingerprint.result?.let {
it.mutableMethod.apply {
// Initialize seekbar method
addInstructions(
1, """
move-object/from16 v0, p0
const-string v1, "$rectangleFieldName"
invoke-static {v0, v1}, $INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarRect(Ljava/lang/Object;Ljava/lang/String;)V
"""
)

// Draw segment
for ((index, instruction) in implementation!!.instructions.withIndex()) {
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) continue

val invokeInstruction = getInstruction<Instruction35c>(index)
if ((invokeInstruction.reference as MethodReference).name != "drawCircle") continue

val (canvasInstance, centerY) =
getInstruction<FiveRegisterInstruction>(
index
).let { drawSegmentInstruction ->
drawSegmentInstruction.registerC to drawSegmentInstruction.registerE
}

addInstruction(
index,
"invoke-static {v$canvasInstance, v$centerY}, $INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->drawSponsorTimeBars(Landroid/graphics/Canvas;F)V"
)
break
}
}
} ?: throw MusicPlaybackControlsTimeBarDrawFingerprint.exception

/**
* Set current video id
*/
VideoIdPatch.injectCall("$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setCurrentVideoId(Ljava/lang/String;)V")
}

private companion object {
const val INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR =
"Lapp/revanced/music/sponsorblock/SegmentPlaybackController;"

lateinit var rectangleFieldName: String
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package app.revanced.patches.music.utils.sponsorblock.resource.patch

import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.utils.annotations.MusicCompatibility
import app.revanced.patches.music.utils.settings.resource.patch.SettingsPatch
import app.revanced.patches.music.utils.sponsorblock.bytecode.patch.SponsorBlockBytecodePatch
import app.revanced.util.resources.MusicResourceHelper
import app.revanced.util.resources.MusicResourceHelper.hookPreference
import app.revanced.util.resources.ResourceUtils
import app.revanced.util.resources.ResourceUtils.copyResources

@Patch
@Name("SponsorBlock")
@Description("Integrates SponsorBlock which allows skipping video segments such as sponsored content.")
@DependsOn(
[
SettingsPatch::class,
SponsorBlockBytecodePatch::class
]
)
@MusicCompatibility
class SponsorBlockPatch : ResourcePatch {
override fun execute(context: ResourceContext) {

/**
* Copy preference
*/
arrayOf(
ResourceUtils.ResourceGroup(
"xml",
"sponsorblock_prefs.xml"
)
).forEach { resourceGroup ->
context.copyResources("music/sponsorblock", resourceGroup)
}

/**
* Hook SponsorBlock preference
*/
context.hookPreference(
"revanced_sponsorblock_settings",
"com.google.android.apps.youtube.music.settings.fragment.AdvancedPrefsFragmentCompat"
)

val publicFile = context["res/values/public.xml"]

publicFile.writeText(
publicFile.readText()
.replace(
"\"advanced_prefs_compat\"",
"\"sponsorblock_prefs\""
)
)

context["res/xml/sponsorblock_prefs.xml"].writeText(
context["res/xml/sponsorblock_prefs.xml"].readText()
.replace("\"com.google.android.apps.youtube.music\"", "\"" + MusicResourceHelper.targetPackage + "\"")
)

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package app.revanced.patches.music.utils.videoinformation.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode

object PlayerControllerSetTimeReferenceFingerprint : MethodFingerprint(
returnType = "V",
opcodes = listOf(
Opcode.INVOKE_DIRECT_RANGE,
Opcode.IGET_OBJECT
),
strings = listOf("Media progress reported outside media playback: ")
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package app.revanced.patches.music.utils.videoinformation.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

object PlayerInitFingerprint : MethodFingerprint(
strings = listOf("playVideo called on player response with no videoStreamingData."),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package app.revanced.patches.music.utils.videoinformation.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

object SeekFingerprint : MethodFingerprint(
strings = listOf("Attempting to seek during an ad")
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package app.revanced.patches.music.utils.videoinformation.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode

object VideoLengthFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_WIDE,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_WIDE,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_WIDE,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT
)
)
Loading

0 comments on commit 88d6389

Please sign in to comment.