mirror of
https://github.com/revanced/revanced-patches
synced 2024-09-03 04:56:05 +02:00
fix(youtube/video-ads): add switch to temporarily fix buffering issues
This commit is contained in:
parent
deeef4e96d
commit
7aa3bce6cc
@ -15,12 +15,13 @@ import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.youtube.ad.video.annotations.VideoAdsCompatibility
|
||||
import app.revanced.patches.youtube.ad.video.fingerprints.LoadVideoAdsFingerprint
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.playback.patch.FixPlaybackPatch
|
||||
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
|
||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference
|
||||
|
||||
@Patch
|
||||
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])
|
||||
@DependsOn([IntegrationsPatch::class, SettingsPatch::class, FixPlaybackPatch::class])
|
||||
@Name("video-ads")
|
||||
@Description("Removes ads in the video player.")
|
||||
@VideoAdsCompatibility
|
||||
|
@ -7,40 +7,41 @@ import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.data.toMethodWalker
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.extensions.replaceInstruction
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
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.PatchResultError
|
||||
import app.revanced.patcher.patch.PatchResultSuccess
|
||||
import app.revanced.patcher.patch.annotations.DependsOn
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.youtube.layout.autocaptions.fingerprints.StartVideoInformerFingerprint
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlockCompatibility
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.bytecode.fingerprints.*
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.resource.patch.SponsorBlockResourcePatch
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
|
||||
import app.revanced.patches.youtube.misc.playercontroller.patch.PlayerControllerPatch
|
||||
import app.revanced.patches.youtube.misc.playercontrols.bytecode.patch.PlayerControlsBytecodePatch
|
||||
import app.revanced.patches.youtube.misc.videoid.patch.VideoIdPatch
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
import org.jf.dexlib2.builder.MutableMethodImplementation
|
||||
import org.jf.dexlib2.iface.instruction.*
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
|
||||
import org.jf.dexlib2.iface.reference.FieldReference
|
||||
import org.jf.dexlib2.iface.reference.MethodReference
|
||||
import org.jf.dexlib2.iface.reference.StringReference
|
||||
import org.jf.dexlib2.immutable.ImmutableMethod
|
||||
import org.jf.dexlib2.immutable.ImmutableMethodParameter
|
||||
import org.jf.dexlib2.util.MethodUtil
|
||||
|
||||
@Patch
|
||||
@DependsOn(
|
||||
dependencies = [PlayerControlsBytecodePatch::class, IntegrationsPatch::class, SponsorBlockResourcePatch::class, VideoIdPatch::class]
|
||||
dependencies = [
|
||||
PlayerControllerPatch::class, // updates video length and adds method to seek in video
|
||||
PlayerControlsBytecodePatch::class,
|
||||
IntegrationsPatch::class,
|
||||
SponsorBlockResourcePatch::class,
|
||||
VideoIdPatch::class
|
||||
]
|
||||
)
|
||||
@Name("sponsorblock")
|
||||
@Description("Integrate SponsorBlock.")
|
||||
@ -53,7 +54,6 @@ class SponsorBlockBytecodePatch : BytecodePatch(
|
||||
VideoTimeFingerprint,
|
||||
NextGenWatchLayoutFingerprint,
|
||||
AppendTimeFingerprint,
|
||||
PlayerInitFingerprint,
|
||||
PlayerOverlaysLayoutInitFingerprint,
|
||||
ShortsPlayerConstructorFingerprint,
|
||||
StartVideoInformerFingerprint
|
||||
@ -160,23 +160,6 @@ class SponsorBlockBytecodePatch : BytecodePatch(
|
||||
"invoke-static {v$canvasInstance, v$centerY}, Lapp/revanced/integrations/sponsorblock/PlayerController;->drawSponsorTimeBars(Landroid/graphics/Canvas;F)V"
|
||||
)
|
||||
|
||||
/*
|
||||
Set video length
|
||||
*/
|
||||
VideoLengthFingerprint.resolve(context, seekbarSignatureResult.classDef)
|
||||
val videoLengthMethodResult = VideoLengthFingerprint.result!!
|
||||
val videoLengthMethod = videoLengthMethodResult.mutableMethod
|
||||
val videoLengthMethodInstructions = videoLengthMethod.implementation!!.instructions
|
||||
|
||||
val videoLengthRegister =
|
||||
(videoLengthMethodInstructions[videoLengthMethodResult.scanResult.patternScanResult!!.endIndex - 2] as OneRegisterInstruction).registerA
|
||||
val dummyRegisterForLong =
|
||||
videoLengthRegister + 1 // this is required for long values since they are 64 bit wide
|
||||
videoLengthMethod.addInstruction(
|
||||
videoLengthMethodResult.scanResult.patternScanResult!!.endIndex,
|
||||
"invoke-static {v$videoLengthRegister, v$dummyRegisterForLong}, Lapp/revanced/integrations/sponsorblock/PlayerController;->setVideoLength(J)V"
|
||||
)
|
||||
|
||||
/*
|
||||
Voting & Shield button
|
||||
*/
|
||||
@ -249,12 +232,7 @@ class SponsorBlockBytecodePatch : BytecodePatch(
|
||||
)
|
||||
|
||||
// initialize the player controller
|
||||
val initFingerprintResult = PlayerInitFingerprint.result!!
|
||||
val initInstanceRegister = 0
|
||||
initFingerprintResult.mutableClass.methods.first { MethodUtil.isConstructor(it) }.addInstruction(
|
||||
4, // after super class invoke
|
||||
"invoke-static {v$initInstanceRegister}, Lapp/revanced/integrations/sponsorblock/PlayerController;->onCreate(Ljava/lang/Object;)V"
|
||||
)
|
||||
PlayerControllerPatch.onCreateHook("Lapp/revanced/integrations/sponsorblock/PlayerController;", "initialize")
|
||||
|
||||
// initialize the sponsorblock view
|
||||
PlayerOverlaysLayoutInitFingerprint.result!!.mutableMethod.addInstruction(
|
||||
@ -262,39 +240,6 @@ class SponsorBlockBytecodePatch : BytecodePatch(
|
||||
"invoke-static {p0}, Lapp/revanced/integrations/sponsorblock/player/ui/SponsorBlockView;->initialize(Ljava/lang/Object;)V"
|
||||
)
|
||||
|
||||
// lastly create hooks for the player controller
|
||||
|
||||
// get original seek method
|
||||
SeekFingerprint.resolve(context, initFingerprintResult.classDef)
|
||||
val seekFingerprintResultMethod = SeekFingerprint.result!!.method
|
||||
// get enum type for the seek helper method
|
||||
val seekSourceEnumType = seekFingerprintResultMethod.parameterTypes[1].toString()
|
||||
|
||||
// create helper method
|
||||
val seekHelperMethod = ImmutableMethod(
|
||||
seekFingerprintResultMethod.definingClass,
|
||||
"seekHelper",
|
||||
listOf(ImmutableMethodParameter("J", null, "time")),
|
||||
"Z",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
null, null,
|
||||
MutableMethodImplementation(4)
|
||||
).toMutable()
|
||||
|
||||
// insert helper method instructions
|
||||
seekHelperMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
|
||||
invoke-virtual {p0, p1, p2, v0}, ${seekFingerprintResultMethod.definingClass}->${seekFingerprintResultMethod.name}(J$seekSourceEnumType)Z
|
||||
move-result p1
|
||||
return p1
|
||||
"""
|
||||
)
|
||||
|
||||
// add the helper method to the original class
|
||||
initFingerprintResult.mutableClass.methods.add(seekHelperMethod)
|
||||
|
||||
// get rectangle field name
|
||||
RectangleFieldInvalidatorFingerprint.resolve(context, seekbarSignatureResult.classDef)
|
||||
val rectangleFieldInvalidatorInstructions =
|
||||
@ -302,23 +247,19 @@ class SponsorBlockBytecodePatch : BytecodePatch(
|
||||
val rectangleFieldName =
|
||||
((rectangleFieldInvalidatorInstructions.elementAt(rectangleFieldInvalidatorInstructions.count() - 3) as ReferenceInstruction).reference as FieldReference).name
|
||||
|
||||
// get the player controller class from the integrations
|
||||
val playerControllerMethods =
|
||||
context.proxy(context.classes.first { it.type.endsWith("PlayerController;") }).mutableClass.methods
|
||||
|
||||
// get the method which contain the "replaceMe" strings
|
||||
val replaceMeMethods =
|
||||
playerControllerMethods.filter { it.name == "onCreate" || it.name == "setSponsorBarRect" }
|
||||
|
||||
// replace the "replaceMeWith*" strings
|
||||
context
|
||||
.proxy(context.classes.first { it.type.endsWith("PlayerController;") })
|
||||
.mutableClass
|
||||
.methods
|
||||
.find { it.name == "setSponsorBarRect" }
|
||||
?.let { method ->
|
||||
fun MutableMethod.replaceStringInstruction(index: Int, instruction: Instruction, with: String) {
|
||||
val register = (instruction as OneRegisterInstruction).registerA
|
||||
this.replaceInstruction(
|
||||
index, "const-string v$register, \"$with\""
|
||||
)
|
||||
}
|
||||
|
||||
// replace the "replaceMeWith*" strings
|
||||
for (method in replaceMeMethods) {
|
||||
for ((index, it) in method.implementation!!.instructions.withIndex()) {
|
||||
if (it.opcode.ordinal != Opcode.CONST_STRING.ordinal) continue
|
||||
|
||||
@ -330,7 +271,7 @@ class SponsorBlockBytecodePatch : BytecodePatch(
|
||||
method.replaceStringInstruction(index, it, "seekHelper")
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?: return PatchResultError("Could not find the method which contains the replaceMeWith* strings")
|
||||
|
||||
val startVideoInformerMethod = StartVideoInformerFingerprint.result!!.mutableMethod
|
||||
startVideoInformerMethod.addInstructions(
|
||||
|
@ -0,0 +1,13 @@
|
||||
package app.revanced.patches.youtube.misc.playback.annotations
|
||||
|
||||
import app.revanced.patcher.annotation.Compatibility
|
||||
import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.41.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class FixPlaybackCompatibility
|
@ -0,0 +1,52 @@
|
||||
package app.revanced.patches.youtube.misc.playback.patch
|
||||
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.PatchResult
|
||||
import app.revanced.patcher.patch.PatchResultSuccess
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotations.DependsOn
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.playback.annotations.FixPlaybackCompatibility
|
||||
import app.revanced.patches.youtube.misc.playercontroller.patch.PlayerControllerPatch
|
||||
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
|
||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.videoid.patch.VideoIdPatch
|
||||
|
||||
@DependsOn([
|
||||
IntegrationsPatch::class,
|
||||
PlayerControllerPatch::class, // updates video length and adds method to seek in video, necessary for this patch
|
||||
SettingsPatch::class,
|
||||
VideoIdPatch::class
|
||||
])
|
||||
@Name("fix-playback")
|
||||
@Description("Fixes the issue with videos not playing when video ads are removed.")
|
||||
@FixPlaybackCompatibility
|
||||
@Version("0.0.1")
|
||||
class FixPlaybackPatch : ResourcePatch {
|
||||
override fun execute(context: ResourceContext): PatchResult {
|
||||
SettingsPatch.PreferenceScreen.MISC.addPreferences(
|
||||
SwitchPreference(
|
||||
"revanced_fix_playback",
|
||||
StringResource("revanced_fix_playback_title", "Fix video playback issues"),
|
||||
false,
|
||||
StringResource(
|
||||
"revanced_fix_playback_summary_on",
|
||||
"The fix is enabled"
|
||||
),
|
||||
StringResource(
|
||||
"revanced_fix_playback_summary_off",
|
||||
"The fix is disabled"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// If a new video loads, fix the playback issue
|
||||
VideoIdPatch.injectCall("Lapp/revanced/integrations/patches/FixPlaybackPatch;->newVideoLoaded(Ljava/lang/String;)V")
|
||||
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package app.revanced.patches.youtube.misc.playercontroller.annotation
|
||||
|
||||
import app.revanced.patcher.annotation.Compatibility
|
||||
import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.41.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class PlayerControllerCompatibility
|
@ -0,0 +1,15 @@
|
||||
package app.revanced.patches.youtube.misc.playercontroller.fingerprint
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlockCompatibility
|
||||
|
||||
@Name("create-video-player-seekbar-fingerprint")
|
||||
@SponsorBlockCompatibility
|
||||
@Version("0.0.1")
|
||||
object CreateVideoPlayerSeekbarFingerprint : MethodFingerprint(
|
||||
"V",
|
||||
strings = listOf("timed_markers_width")
|
||||
)
|
@ -1,4 +1,4 @@
|
||||
package app.revanced.patches.youtube.layout.sponsorblock.bytecode.fingerprints
|
||||
package app.revanced.patches.youtube.misc.playercontroller.fingerprint
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
@ -1,4 +1,4 @@
|
||||
package app.revanced.patches.youtube.layout.sponsorblock.bytecode.fingerprints
|
||||
package app.revanced.patches.youtube.misc.playercontroller.fingerprint
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
@ -1,4 +1,4 @@
|
||||
package app.revanced.patches.youtube.layout.sponsorblock.bytecode.fingerprints
|
||||
package app.revanced.patches.youtube.misc.playercontroller.fingerprint
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
@ -0,0 +1,116 @@
|
||||
package app.revanced.patches.youtube.misc.playercontroller.patch
|
||||
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.instruction
|
||||
import app.revanced.patcher.extensions.or
|
||||
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
|
||||
import app.revanced.patcher.patch.annotations.DependsOn
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.playercontroller.annotation.PlayerControllerCompatibility
|
||||
import app.revanced.patches.youtube.misc.playercontroller.fingerprint.CreateVideoPlayerSeekbarFingerprint
|
||||
import app.revanced.patches.youtube.misc.playercontroller.fingerprint.PlayerInitFingerprint
|
||||
import app.revanced.patches.youtube.misc.playercontroller.fingerprint.SeekFingerprint
|
||||
import app.revanced.patches.youtube.misc.playercontroller.fingerprint.VideoLengthFingerprint
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.builder.MutableMethodImplementation
|
||||
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import org.jf.dexlib2.immutable.ImmutableMethod
|
||||
import org.jf.dexlib2.immutable.ImmutableMethodParameter
|
||||
import org.jf.dexlib2.util.MethodUtil
|
||||
|
||||
@Name("player-controller-hook")
|
||||
@Description("Hooks the player controller")
|
||||
@PlayerControllerCompatibility
|
||||
@Version("0.0.1")
|
||||
@DependsOn([IntegrationsPatch::class])
|
||||
class PlayerControllerPatch : BytecodePatch(
|
||||
listOf(
|
||||
PlayerInitFingerprint,
|
||||
CreateVideoPlayerSeekbarFingerprint
|
||||
)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext): PatchResult {
|
||||
with(PlayerInitFingerprint.result!!) {
|
||||
playerInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
|
||||
|
||||
// hook the player controller for use through integrations
|
||||
onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "playerController_onCreateHook")
|
||||
|
||||
// seek method
|
||||
val seekFingerprintResultMethod = SeekFingerprint.also { it.resolve(context, classDef) }.result!!.method
|
||||
|
||||
// create helper method
|
||||
val seekHelperMethod = ImmutableMethod(
|
||||
seekFingerprintResultMethod.definingClass,
|
||||
"seekTo",
|
||||
listOf(ImmutableMethodParameter("J", null, "time")),
|
||||
"Z",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
null, null,
|
||||
MutableMethodImplementation(4)
|
||||
).toMutable()
|
||||
|
||||
// get enum type for the seek helper method
|
||||
val seekSourceEnumType = seekFingerprintResultMethod.parameterTypes[1].toString()
|
||||
|
||||
// insert helper method instructions
|
||||
seekHelperMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
|
||||
invoke-virtual {p0, p1, p2, v0}, ${seekFingerprintResultMethod.definingClass}->${seekFingerprintResultMethod.name}(J$seekSourceEnumType)Z
|
||||
move-result p1
|
||||
return p1
|
||||
"""
|
||||
)
|
||||
|
||||
// add the seekTo method to the class for the integrations to call
|
||||
mutableClass.methods.add(seekHelperMethod)
|
||||
}
|
||||
|
||||
with(CreateVideoPlayerSeekbarFingerprint.result!!) {
|
||||
val videoLengthMethodResult = VideoLengthFingerprint.also { it.resolve(context, classDef) }.result!!
|
||||
|
||||
with(videoLengthMethodResult.mutableMethod) {
|
||||
val videoLengthRegisterIndex = videoLengthMethodResult.scanResult.patternScanResult!!.endIndex - 2
|
||||
val videoLengthRegister = (instruction(videoLengthRegisterIndex) as OneRegisterInstruction).registerA
|
||||
val dummyRegisterForLong = videoLengthRegister + 1 // required for long values since they are wide
|
||||
|
||||
addInstruction(
|
||||
videoLengthMethodResult.scanResult.patternScanResult!!.endIndex,
|
||||
"invoke-static {v$videoLengthRegister, v$dummyRegisterForLong}, $INTEGRATIONS_CLASS_DESCRIPTOR->setCurrentVideoLength(J)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/patches/PlayerControllerPatch;"
|
||||
private const val INSERT_INDEX = 4
|
||||
|
||||
private lateinit var playerInitMethod: MutableMethod
|
||||
|
||||
/**
|
||||
* Hook the player controller.
|
||||
*
|
||||
* @param targetMethodClass The descriptor for the static method to invoke when the player controller is created.
|
||||
*/
|
||||
internal fun onCreateHook(targetMethodClass: String, targetMethodName: String) =
|
||||
playerInitMethod.addInstruction(
|
||||
INSERT_INDEX,
|
||||
"invoke-static { v0 }, $targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
|
||||
)
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ import app.revanced.patches.youtube.misc.videoid.fingerprint.VideoIdFingerprint
|
||||
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Name("video-id-hook")
|
||||
@Description("Hook to detect when the video id changes")
|
||||
@Description("Hooks to detect when the video id changes")
|
||||
@VideoIdCompatibility
|
||||
@Version("0.0.1")
|
||||
@DependsOn([IntegrationsPatch::class])
|
||||
|
Loading…
Reference in New Issue
Block a user