Compare commits

...

5 Commits

Author SHA1 Message Date
semantic-release-bot
038d6b215a chore(release): 2.188.0-dev.17 [skip ci]
# [2.188.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v2.188.0-dev.16...v2.188.0-dev.17) (2023-08-21)

### Features

* **YouTube:** Add `Alternative thumbnails` patch ([#2834](https://github.com/ReVanced/revanced-patches/issues/2834)) ([4f7618c](4f7618c980))
2023-08-21 06:31:19 +00:00
LisoUseInAIKyrios
4f7618c980 feat(YouTube): Add Alternative thumbnails patch (#2834)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-08-21 10:28:34 +04:00
Pun Butrach
9893c99009 build: update Gradle to v8.3 (#2843) 2023-08-19 15:37:51 +02:00
semantic-release-bot
30ac1f6954 chore(release): 2.188.0-dev.16 [skip ci]
# [2.188.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v2.188.0-dev.15...v2.188.0-dev.16) (2023-08-19)

### Bug Fixes

* **YouTube Music - Remove upgrade button:** Remove the correct navigation bar item ([395cfd2](395cfd2ee2))
2023-08-19 00:51:44 +00:00
oSumAtrIX
395cfd2ee2 fix(YouTube Music - Remove upgrade button): Remove the correct navigation bar item
A new item has been added to the list, so the index has to be shifted by one. This is not a final solution and the last index should be removed dynamically
2023-08-19 02:48:21 +02:00
12 changed files with 265 additions and 5 deletions

View File

@@ -1,3 +1,17 @@
# [2.188.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v2.188.0-dev.16...v2.188.0-dev.17) (2023-08-21)
### Features
* **YouTube:** Add `Alternative thumbnails` patch ([#2834](https://github.com/ReVanced/revanced-patches/issues/2834)) ([8a4277c](https://github.com/ReVanced/revanced-patches/commit/8a4277c486d995f57cde3a56274979c4a7b42bf6))
# [2.188.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v2.188.0-dev.15...v2.188.0-dev.16) (2023-08-19)
### Bug Fixes
* **YouTube Music - Remove upgrade button:** Remove the correct navigation bar item ([fd3813f](https://github.com/ReVanced/revanced-patches/commit/fd3813f66ededdae1e52bf021a3de9d818f65096))
# [2.188.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v2.188.0-dev.14...v2.188.0-dev.15) (2023-08-14)
# [2.188.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v2.188.0-dev.13...v2.188.0-dev.14) (2023-08-13)

View File

@@ -1,4 +1,4 @@
org.gradle.parallel = true
org.gradle.caching = true
kotlin.code.style = official
version = 2.188.0-dev.15
version = 2.188.0-dev.17

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

File diff suppressed because one or more lines are too long

View File

@@ -18,7 +18,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
@Patch
@Name("Upgrade button remover")
@Name("Remove upgrade button")
@Description("Removes the upgrade tab from the pivot bar.")
@MusicCompatibility
class RemoveUpgradeButtonPatch : BytecodePatch(
@@ -36,7 +36,7 @@ class RemoveUpgradeButtonPatch : BytecodePatch(
val instructionList = """
invoke-interface { v0 }, Ljava/util/List;->size()I
move-result v1
const/4 v2, 0x3
const/4 v2, 0x4
invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
iput-object v0, v$register, $pivotBarElementFieldRef
""".toInstructions().toMutableList()

View File

@@ -0,0 +1,8 @@
package app.revanced.patches.youtube.layout.alternativethumbnails.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.google.android.youtube", arrayOf("18.20.39", "18.23.35"))])
@Target(AnnotationTarget.CLASS)
internal annotation class AlternativeThumbnailsCompatibility

View File

@@ -0,0 +1,14 @@
package app.revanced.patches.youtube.layout.alternativethumbnails.bytecode.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
object CronetURLRequestCallbackOnFailureFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;", "Lorg/chromium/net/CronetException;"),
customFingerprint = { methodDef, _ ->
methodDef.name == "onFailed"
}
)

View File

@@ -0,0 +1,21 @@
package app.revanced.patches.youtube.layout.alternativethumbnails.bytecode.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
// Acts as a parent fingerprint.
object CronetURLRequestCallbackOnResponseStartedFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;"),
strings = listOf(
"Content-Length",
"Content-Type",
"identity",
"application/x-protobuf"
),
customFingerprint = { methodDef, _ ->
methodDef.name == "onResponseStarted"
}
)

View File

@@ -0,0 +1,14 @@
package app.revanced.patches.youtube.layout.alternativethumbnails.bytecode.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
object CronetURLRequestCallbackOnSucceededFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;"),
customFingerprint = { methodDef, _ ->
methodDef.name == "onSucceeded"
}
)

View File

@@ -0,0 +1,10 @@
package app.revanced.patches.youtube.layout.alternativethumbnails.bytecode.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
object MessageDigestImageUrlFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("Ljava/lang/String;", "L")
)

View File

@@ -0,0 +1,12 @@
package app.revanced.patches.youtube.layout.alternativethumbnails.bytecode.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
object MessageDigestImageUrlParentFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Ljava/lang/String;",
parameters = listOf(),
strings = listOf("@#&=*+-_.,:!?()/~'%;\$"),
)

View File

@@ -0,0 +1,167 @@
package app.revanced.patches.youtube.layout.alternativethumbnails.bytecode.patch
import app.revanced.extensions.toErrorResult
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
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.patch.annotations.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.shared.settings.preference.impl.ArrayResource
import app.revanced.patches.shared.settings.preference.impl.ListPreference
import app.revanced.patches.shared.settings.preference.impl.NonInteractivePreference
import app.revanced.patches.shared.settings.preference.impl.PreferenceScreen
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import app.revanced.patches.youtube.layout.alternativethumbnails.annotations.AlternativeThumbnailsCompatibility
import app.revanced.patches.youtube.layout.alternativethumbnails.bytecode.fingerprints.CronetURLRequestCallbackOnFailureFingerprint
import app.revanced.patches.youtube.layout.alternativethumbnails.bytecode.fingerprints.CronetURLRequestCallbackOnResponseStartedFingerprint
import app.revanced.patches.youtube.layout.alternativethumbnails.bytecode.fingerprints.CronetURLRequestCallbackOnSucceededFingerprint
import app.revanced.patches.youtube.layout.alternativethumbnails.bytecode.fingerprints.MessageDigestImageUrlFingerprint
import app.revanced.patches.youtube.layout.alternativethumbnails.bytecode.fingerprints.MessageDigestImageUrlParentFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])
@Name("Alternative thumbnails")
@AlternativeThumbnailsCompatibility
@Description("Adds an option to replace video thumbnails with still image captures of the video.")
class AlternativeThumbnailsPatch : BytecodePatch(
listOf(MessageDigestImageUrlParentFingerprint, CronetURLRequestCallbackOnResponseStartedFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(
PreferenceScreen(
"revanced_alt_thumbnails_preference_screen",
StringResource("revanced_alt_thumbnails_preference_screen_title", "Alternative thumbnails"),
listOf(
SwitchPreference(
"revanced_alt_thumbnail",
StringResource("revanced_alt_thumbnail_title", "Enable alternative thumbnails"),
StringResource("revanced_alt_thumbnail_summary_on", "YouTube video stills shown"),
StringResource("revanced_alt_thumbnail_summary_off", "Original YouTube thumbnails shown")
),
ListPreference(
"revanced_alt_thumbnail_type",
StringResource("revanced_alt_thumbnail_type_title", "Video time to take the still from"),
ArrayResource(
"revanced_alt_thumbnail_type_entries",
listOf(
StringResource("revanced_alt_thumbnail_type_entry_1", "Beginning of video"),
StringResource("revanced_alt_thumbnail_type_entry_2", "Middle of video"),
StringResource("revanced_alt_thumbnail_type_entry_3", "End of video"),
)
),
ArrayResource(
"revanced_alt_thumbnail_type_entry_values",
listOf(
StringResource("revanced_alt_thumbnail_type_entry_value_1", "1"),
StringResource("revanced_alt_thumbnail_type_entry_value_2", "2"),
StringResource("revanced_alt_thumbnail_type_entry_value_3", "3"),
)
)
),
SwitchPreference(
"revanced_alt_thumbnail_fast_quality",
StringResource("revanced_alt_thumbnail_fast_quality_title", "Use fast alternative thumbnails"),
StringResource("revanced_alt_thumbnail_fast_quality_summary_on", "Using medium quality stills. Thumbnails will load faster, but live streams, unreleased, or very old videos may show blank thumbnails"),
StringResource("revanced_alt_thumbnail_fast_quality_summary_off", "Using high quality stills")
),
NonInteractivePreference(
StringResource("revanced_alt_thumbnail_about_title", "About"),
StringResource("revanced_alt_thumbnail_about_summary", "Alternative thumbnails are still images from the beginning/middle/end of each video. No external API is used, as these images are built into YouTube")
)
),
StringResource("revanced_alt_thumbnails_preference_screen_summary", "Video thumbnail settings")
)
)
MessageDigestImageUrlParentFingerprint.result
?: return MessageDigestImageUrlParentFingerprint.toErrorResult()
MessageDigestImageUrlFingerprint.resolve(context, MessageDigestImageUrlParentFingerprint.result!!.classDef)
MessageDigestImageUrlFingerprint.result?.apply {
loadImageUrlMethod = mutableMethod
} ?: return MessageDigestImageUrlFingerprint.toErrorResult()
addImageUrlHook(INTEGRATIONS_CLASS_DESCRIPTOR, true)
CronetURLRequestCallbackOnResponseStartedFingerprint.result
?: return CronetURLRequestCallbackOnResponseStartedFingerprint.toErrorResult()
CronetURLRequestCallbackOnSucceededFingerprint.resolve(
context,
CronetURLRequestCallbackOnResponseStartedFingerprint.result!!.classDef
)
CronetURLRequestCallbackOnSucceededFingerprint.result?.apply {
loadImageSuccessCallbackMethod = mutableMethod
} ?: return CronetURLRequestCallbackOnSucceededFingerprint.toErrorResult()
addImageUrlSuccessCallbackHook(INTEGRATIONS_CLASS_DESCRIPTOR)
CronetURLRequestCallbackOnFailureFingerprint.resolve(
context,
CronetURLRequestCallbackOnResponseStartedFingerprint.result!!.classDef
)
CronetURLRequestCallbackOnFailureFingerprint.result?.apply {
loadImageErrorCallbackMethod = mutableMethod
} ?: return CronetURLRequestCallbackOnFailureFingerprint.toErrorResult()
return PatchResultSuccess()
}
internal companion object {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/patches/AlternativeThumbnailsPatch;"
private lateinit var loadImageUrlMethod: MutableMethod
private var loadImageUrlIndex = 0
private lateinit var loadImageSuccessCallbackMethod: MutableMethod
private var loadImageSuccessCallbackIndex = 0
private lateinit var loadImageErrorCallbackMethod: MutableMethod
private var loadImageErrorCallbackIndex = 0
/**
* @param highPriority If the hook should be called before all other hooks.
*/
fun addImageUrlHook(targetMethodClass: String, highPriority: Boolean) {
loadImageUrlMethod.addInstructions(
if (highPriority) 0 else loadImageUrlIndex, """
invoke-static { p1 }, $targetMethodClass->overrideImageURL(Ljava/lang/String;)Ljava/lang/String;
move-result-object p1
"""
)
loadImageUrlIndex += 2
}
/**
* If a connection completed, which includes normal 200 responses but also includes
* status 404 and other error like http responses.
*/
fun addImageUrlSuccessCallbackHook(targetMethodClass: String) {
loadImageSuccessCallbackMethod.addInstruction(
loadImageSuccessCallbackIndex++,
"invoke-static { p2 }, $targetMethodClass->handleCronetSuccess(Lorg/chromium/net/UrlResponseInfo;)V"
)
}
/**
* If a connection outright failed to complete any connection.
*/
fun addImageUrlErrorCallbackHook(targetMethodClass: String) {
loadImageErrorCallbackMethod.addInstruction(
loadImageErrorCallbackIndex++,
"invoke-static { p2, p3 }, $targetMethodClass->handleCronetFailure(Lorg/chromium/net/UrlResponseInfo;Ljava/io/IOException;)V"
)
}
}
}