feat(YouTube - Alternative Thumbnails): Add option to use DeArrow (#3378)

Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
This commit is contained in:
Chris 2023-12-11 02:07:09 +01:00 committed by GitHub
parent 314f843002
commit 41217f61e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 233 additions and 71 deletions

View File

@ -657,7 +657,9 @@ public final class app/revanced/patches/shared/settings/preference/impl/ListPref
} }
public final class app/revanced/patches/shared/settings/preference/impl/NonInteractivePreference : app/revanced/patches/shared/settings/preference/BasePreference { public final class app/revanced/patches/shared/settings/preference/impl/NonInteractivePreference : app/revanced/patches/shared/settings/preference/BasePreference {
public fun <init> (Lapp/revanced/patches/shared/settings/preference/impl/StringResource;Lapp/revanced/patches/shared/settings/preference/impl/StringResource;)V public fun <init> (Lapp/revanced/patches/shared/settings/preference/impl/StringResource;Lapp/revanced/patches/shared/settings/preference/impl/StringResource;Ljava/lang/String;Z)V
public synthetic fun <init> (Lapp/revanced/patches/shared/settings/preference/impl/StringResource;Lapp/revanced/patches/shared/settings/preference/impl/StringResource;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getSelectable ()Z
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
} }
@ -1363,7 +1365,6 @@ public final class app/revanced/patches/youtube/layout/theme/ThemeBytecodePatch
public final class app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch : app/revanced/patcher/patch/BytecodePatch { public final class app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch; public static final field INSTANCE Lapp/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch;
public final fun addImageUrlErrorCallbackHook (Ljava/lang/String;)V
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
} }

View File

@ -9,7 +9,7 @@ import org.w3c.dom.Element
* *
* @param key The key of the preference. * @param key The key of the preference.
* @param title The title of the preference. * @param title The title of the preference.
* @param tag The tag of the preference. * @param tag The full class name for the preference.
* @param summary The summary of the preference. * @param summary The summary of the preference.
*/ */
abstract class BasePreference( abstract class BasePreference(

View File

@ -14,15 +14,21 @@ import org.w3c.dom.Element
* *
* @param title The title of the preference. * @param title The title of the preference.
* @param summary The summary of the text preference. * @param summary The summary of the text preference.
* @param selectable If this preference responds to tapping.
* Setting to 'true' restores the horizontal dividers on the top and bottom,
* but tapping will still do nothing since this Preference has no key.
*/ */
class NonInteractivePreference( class NonInteractivePreference(
title: StringResource, title: StringResource,
summary: StringResource, summary: StringResource?,
) : BasePreference(null, title, summary, "Preference") { tag: String = "Preference",
// If androidx.preference is later used, this can be changed to the show top/bottom dividers feature.
val selectable: Boolean = false
) : BasePreference(null, title, summary, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit): Element { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit): Element {
return super.serialize(ownerDocument, resourceCallback).apply { return super.serialize(ownerDocument, resourceCallback).apply {
addSummary(summary?.also { resourceCallback.invoke(it) addSummary(summary?.also { resourceCallback.invoke(it)
setAttribute("android:selectable", false.toString()) setAttribute("android:selectable", selectable.toString())
}) })
} }
} }

View File

@ -1,22 +1,36 @@
package app.revanced.patches.youtube.layout.thumbnails package app.revanced.patches.youtube.layout.thumbnails
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.shared.settings.preference.impl.* import app.revanced.patches.shared.settings.preference.impl.*
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.* import app.revanced.patches.youtube.layout.thumbnails.fingerprints.MessageDigestImageUrlFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.MessageDigestImageUrlParentFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.RequestFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnFailureFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnResponseStartedFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnSucceededFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
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.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
@Patch( @Patch(
name = "Alternative thumbnails", name = "Alternative thumbnails",
description = "Adds options to replace video thumbnails with still image captures of the video.", description = "Adds options to replace video thumbnails with still image captures of the video.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class], dependencies = [IntegrationsPatch::class, SettingsPatch::class, AlternativeThumbnailsResourcePatch::class],
compatiblePackages = [ compatiblePackages = [
CompatiblePackage( CompatiblePackage(
"com.google.android.youtube", "com.google.android.youtube",
@ -34,7 +48,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
) )
@Suppress("unused") @Suppress("unused")
object AlternativeThumbnailsPatch : BytecodePatch( object AlternativeThumbnailsPatch : BytecodePatch(
setOf(MessageDigestImageUrlParentFingerprint, CronetURLRequestCallbackOnResponseStartedFingerprint) setOf(
MessageDigestImageUrlParentFingerprint,
OnResponseStartedFingerprint,
RequestFingerprint,
)
) { ) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/patches/AlternativeThumbnailsPatch;" "Lapp/revanced/integrations/patches/AlternativeThumbnailsPatch;"
@ -51,9 +69,11 @@ object AlternativeThumbnailsPatch : BytecodePatch(
/** /**
* @param highPriority If the hook should be called before all other hooks. * @param highPriority If the hook should be called before all other hooks.
*/ */
@Suppress("SameParameterValue")
private fun addImageUrlHook(targetMethodClass: String, highPriority: Boolean) { private fun addImageUrlHook(targetMethodClass: String, highPriority: Boolean) {
loadImageUrlMethod.addInstructions( loadImageUrlMethod.addInstructions(
if (highPriority) 0 else loadImageUrlIndex, """ if (highPriority) 0 else loadImageUrlIndex,
"""
invoke-static { p1 }, $targetMethodClass->overrideImageURL(Ljava/lang/String;)Ljava/lang/String; invoke-static { p1 }, $targetMethodClass->overrideImageURL(Ljava/lang/String;)Ljava/lang/String;
move-result-object p1 move-result-object p1
""" """
@ -65,103 +85,197 @@ object AlternativeThumbnailsPatch : BytecodePatch(
* If a connection completed, which includes normal 200 responses but also includes * If a connection completed, which includes normal 200 responses but also includes
* status 404 and other error like http responses. * status 404 and other error like http responses.
*/ */
@Suppress("SameParameterValue")
private fun addImageUrlSuccessCallbackHook(targetMethodClass: String) { private fun addImageUrlSuccessCallbackHook(targetMethodClass: String) {
loadImageSuccessCallbackMethod.addInstruction( loadImageSuccessCallbackMethod.addInstruction(
loadImageSuccessCallbackIndex++, loadImageSuccessCallbackIndex++,
"invoke-static { p2 }, $targetMethodClass->handleCronetSuccess(Lorg/chromium/net/UrlResponseInfo;)V" "invoke-static { p1, p2 }, $targetMethodClass->handleCronetSuccess(" +
"Lorg/chromium/net/UrlRequest;Lorg/chromium/net/UrlResponseInfo;)V"
) )
} }
/** /**
* If a connection outright failed to complete any connection. * If a connection outright failed to complete any connection.
*/ */
fun addImageUrlErrorCallbackHook(targetMethodClass: String) { @Suppress("SameParameterValue")
private fun addImageUrlErrorCallbackHook(targetMethodClass: String) {
loadImageErrorCallbackMethod.addInstruction( loadImageErrorCallbackMethod.addInstruction(
loadImageErrorCallbackIndex++, loadImageErrorCallbackIndex++,
"invoke-static { p2, p3 }, $targetMethodClass->handleCronetFailure(Lorg/chromium/net/UrlResponseInfo;Ljava/io/IOException;)V" "invoke-static { p1, p2, p3 }, $targetMethodClass->handleCronetFailure(" +
"Lorg/chromium/net/UrlRequest;Lorg/chromium/net/UrlResponseInfo;Ljava/io/IOException;)V"
) )
} }
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences( SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(
PreferenceScreen( PreferenceScreen(
"revanced_alt_thumbnails_preference_screen", "revanced_alt_thumbnail_preference_screen",
StringResource("revanced_alt_thumbnails_preference_screen_title", "Alternative thumbnails"), StringResource("revanced_alt_thumbnail_preference_screen_title", "Alternative thumbnails"),
listOf( listOf(
NonInteractivePreference(
StringResource("revanced_alt_thumbnail_about_title", "Thumbnails in use"),
null, // Summary is dynamically updated based on the current settings.
tag = "app.revanced.integrations.settingsmenu.AlternativeThumbnailsStatusPreference"
),
SwitchPreference( SwitchPreference(
"revanced_alt_thumbnail", "revanced_alt_thumbnail_dearrow",
StringResource("revanced_alt_thumbnail_title", "Enable alternative thumbnails"), StringResource("revanced_alt_thumbnail_dearrow_title", "Enable DeArrow"),
StringResource("revanced_alt_thumbnail_summary_on", "YouTube video stills shown"), StringResource("revanced_alt_thumbnail_dearrow_summary_on", "Using DeArrow"),
StringResource("revanced_alt_thumbnail_summary_off", "Original YouTube thumbnails shown") StringResource("revanced_alt_thumbnail_dearrow_summary_off", "Not using DeArrow")
),
SwitchPreference(
"revanced_alt_thumbnail_dearrow_connection_toast",
StringResource("revanced_alt_thumbnail_dearrow_connection_toast_title", "Show toast if API is not available"),
StringResource("revanced_alt_thumbnail_dearrow_connection_toast_summary_on", "Toast shown if DeArrow is not available"),
StringResource("revanced_alt_thumbnail_dearrow_connection_toast_summary_off", "Toast not shown if DeArrow is not available")
),
TextPreference(
"revanced_alt_thumbnail_dearrow_api_url",
StringResource(
"revanced_alt_thumbnail_dearrow_api_url_title",
"DeArrow API endpoint"
),
StringResource(
"revanced_alt_thumbnail_dearrow_api_url_summary",
"The URL of the DeArrow thumbnail cache endpoint. " +
"Do not change this unless you know what you\\\'re doing"
),
),
NonInteractivePreference(
StringResource(
"revanced_alt_thumbnail_dearrow_about_title",
"About DeArrow"
),
StringResource(
"revanced_alt_thumbnail_dearrow_about_summary",
"DeArrow provides crowd sourced thumbnails for YouTube videos. " +
"These thumbnails are often more relevant than those provided by YouTube. " +
"If enabled, video URLs will be sent to the API server and no other data is sent."
+ "\\n\\nTap here to learn more about DeArrow"
),
// Custom about preference with link to the DeArrow website.
tag = "app.revanced.integrations.settingsmenu.AlternativeThumbnailsAboutDeArrowPreference",
selectable = true
),
SwitchPreference(
"revanced_alt_thumbnail_stills",
StringResource("revanced_alt_thumbnail_stills_title", "Enable still video captures"),
StringResource("revanced_alt_thumbnail_stills_summary_on", "Using YouTube video still captures"),
StringResource("revanced_alt_thumbnail_stills_summary_off", "Not using YouTube video still captures")
), ),
ListPreference( ListPreference(
"revanced_alt_thumbnail_type", "revanced_alt_thumbnail_stills_time",
StringResource("revanced_alt_thumbnail_type_title", "Video time to take the still from"), StringResource("revanced_alt_thumbnail_stills_time_title", "Video time to take the still from"),
ArrayResource( ArrayResource(
"revanced_alt_thumbnail_type_entries", "revanced_alt_thumbnail_type_entries",
listOf( listOf(
StringResource("revanced_alt_thumbnail_type_entry_1", "Beginning of video"), StringResource("revanced_alt_thumbnail_stills_time_entry_1", "Beginning of video"),
StringResource("revanced_alt_thumbnail_type_entry_2", "Middle of video"), StringResource("revanced_alt_thumbnail_stills_time_entry_2", "Middle of video"),
StringResource("revanced_alt_thumbnail_type_entry_3", "End of video"), StringResource("revanced_alt_thumbnail_stills_time_entry_3", "End of video"),
) )
), ),
ArrayResource( ArrayResource(
"revanced_alt_thumbnail_type_entry_values", "revanced_alt_thumbnail_stills_time_entry_values",
listOf( listOf(
StringResource("revanced_alt_thumbnail_type_entry_value_1", "1"), StringResource("revanced_alt_thumbnail_stills_time_entry_value_1", "1"),
StringResource("revanced_alt_thumbnail_type_entry_value_2", "2"), StringResource("revanced_alt_thumbnail_stills_time_entry_value_2", "2"),
StringResource("revanced_alt_thumbnail_type_entry_value_3", "3"), StringResource("revanced_alt_thumbnail_stills_time_entry_value_3", "3"),
) )
) )
), ),
SwitchPreference( SwitchPreference(
"revanced_alt_thumbnail_fast_quality", "revanced_alt_thumbnail_stills_fast",
StringResource("revanced_alt_thumbnail_fast_quality_title", "Use fast alternative thumbnails"),
StringResource( StringResource(
"revanced_alt_thumbnail_fast_quality_summary_on", "revanced_alt_thumbnail_stills_fast_title",
"Using medium quality stills. Thumbnails will load faster, but live streams, unreleased, or very old videos may show blank thumbnails" "Use fast still captures"
), ),
StringResource("revanced_alt_thumbnail_fast_quality_summary_off", "Using high quality stills") StringResource(
"revanced_alt_thumbnail_stills_fast_summary_on",
"Using medium quality still captures. " +
"Thumbnails will load faster, but live streams, unreleased, " +
"or very old videos may show blank thumbnails"
),
StringResource(
"revanced_alt_thumbnail_stills_fast_summary_off",
"Using high quality still captures"
)
), ),
NonInteractivePreference( NonInteractivePreference(
StringResource("revanced_alt_thumbnail_about_title", "About"),
StringResource( StringResource(
"revanced_alt_thumbnail_about_summary", "revanced_alt_thumbnail_stills_about_title",
"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" "About still video captures"
) ),
StringResource(
"revanced_alt_thumbnail_stills_about_summary",
"Still captures are taken from the beginning/middle/end of each video. " +
"These images are built into YouTube and no external API is used"
),
// Restore the preference dividers to keep it from looking weird.
selectable = true
) )
), ),
StringResource("revanced_alt_thumbnails_preference_screen_summary", "Video thumbnail settings") StringResource("revanced_alt_thumbnail_preference_screen_summary", "Video thumbnail settings")
) )
) )
MessageDigestImageUrlParentFingerprint.result fun MethodFingerprint.getResultOrThrow() =
?: throw MessageDigestImageUrlParentFingerprint.exception result ?: throw exception
MessageDigestImageUrlFingerprint.resolve(context, MessageDigestImageUrlParentFingerprint.result!!.classDef)
MessageDigestImageUrlFingerprint.result?.apply { fun MethodFingerprint.alsoResolve(fingerprint: MethodFingerprint) =
loadImageUrlMethod = mutableMethod also { resolve(context, fingerprint.getResultOrThrow().classDef) }.getResultOrThrow()
} ?: throw MessageDigestImageUrlFingerprint.exception
fun MethodFingerprint.resolveAndLetMutableMethod(
fingerprint: MethodFingerprint,
block: (MutableMethod) -> Unit
) = alsoResolve(fingerprint).also { block(it.mutableMethod) }
MessageDigestImageUrlFingerprint.resolveAndLetMutableMethod(MessageDigestImageUrlParentFingerprint) {
loadImageUrlMethod = it
addImageUrlHook(INTEGRATIONS_CLASS_DESCRIPTOR, true) addImageUrlHook(INTEGRATIONS_CLASS_DESCRIPTOR, true)
}
OnSucceededFingerprint.resolveAndLetMutableMethod(OnResponseStartedFingerprint) {
CronetURLRequestCallbackOnResponseStartedFingerprint.result loadImageSuccessCallbackMethod = it
?: throw CronetURLRequestCallbackOnResponseStartedFingerprint.exception
CronetURLRequestCallbackOnSucceededFingerprint.resolve(
context,
CronetURLRequestCallbackOnResponseStartedFingerprint.result!!.classDef
)
CronetURLRequestCallbackOnSucceededFingerprint.result?.apply {
loadImageSuccessCallbackMethod = mutableMethod
} ?: throw CronetURLRequestCallbackOnSucceededFingerprint.exception
addImageUrlSuccessCallbackHook(INTEGRATIONS_CLASS_DESCRIPTOR) addImageUrlSuccessCallbackHook(INTEGRATIONS_CLASS_DESCRIPTOR)
}
OnFailureFingerprint.resolveAndLetMutableMethod(OnResponseStartedFingerprint) {
loadImageErrorCallbackMethod = it
addImageUrlErrorCallbackHook(INTEGRATIONS_CLASS_DESCRIPTOR)
}
CronetURLRequestCallbackOnFailureFingerprint.resolve( // The URL is required for the failure callback hook, but the URL field is obfuscated.
context, // Add a helper get method that returns the URL field.
CronetURLRequestCallbackOnResponseStartedFingerprint.result!!.classDef RequestFingerprint.getResultOrThrow().apply {
// The url is the only string field that is set inside the constructor.
val urlFieldInstruction = mutableMethod.getInstructions().first {
if (it.opcode != Opcode.IPUT_OBJECT) return@first false
val reference = (it as ReferenceInstruction).reference as FieldReference
reference.type == "Ljava/lang/String;"
} as ReferenceInstruction
val urlFieldName = (urlFieldInstruction.reference as FieldReference).name
val definingClass = RequestFingerprint.IMPLEMENTATION_CLASS_NAME
val addedMethodName = "getHookedUrl"
mutableClass.methods.add(
ImmutableMethod(
definingClass,
addedMethodName,
emptyList(),
"Ljava/lang/String;",
AccessFlags.PUBLIC.value,
null,
null,
MutableMethodImplementation(2)
).toMutable().apply {
addInstructions(
"""
iget-object v0, p0, $definingClass->${urlFieldName}:Ljava/lang/String;
return-object v0
"""
) )
CronetURLRequestCallbackOnFailureFingerprint.result?.apply { })
loadImageErrorCallbackMethod = mutableMethod }
} ?: throw CronetURLRequestCallbackOnFailureFingerprint.exception
} }
} }

View File

@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.thumbnails
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.mergeStrings
@Patch(
dependencies = [SettingsPatch::class]
)
internal object AlternativeThumbnailsResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
context.mergeStrings("alternativethumbnails/host/values/strings.xml")
}
}

View File

@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.RequestFingerprint.IMPLEMENTATION_CLASS_NAME
import com.android.tools.smali.dexlib2.AccessFlags
internal object RequestFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
customFingerprint = { _, classDef ->
classDef.type == IMPLEMENTATION_CLASS_NAME
}
) {
const val IMPLEMENTATION_CLASS_NAME = "Lorg/chromium/net/impl/CronetUrlRequest;"
}

View File

@ -1,10 +1,10 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints package app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
internal object CronetURLRequestCallbackOnFailureFingerprint : MethodFingerprint( internal object OnFailureFingerprint : MethodFingerprint(
returnType = "V", returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;", "Lorg/chromium/net/CronetException;"), parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;", "Lorg/chromium/net/CronetException;"),

View File

@ -1,11 +1,11 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints package app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
// Acts as a parent fingerprint. // Acts as a parent fingerprint.
internal object CronetURLRequestCallbackOnResponseStartedFingerprint : MethodFingerprint( internal object OnResponseStartedFingerprint : MethodFingerprint(
returnType = "V", returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;"), parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;"),

View File

@ -1,10 +1,10 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints package app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
internal object CronetURLRequestCallbackOnSucceededFingerprint : MethodFingerprint( internal object OnSucceededFingerprint : MethodFingerprint(
returnType = "V", returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;"), parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;"),

View File

@ -0,0 +1,9 @@
<resources>
<string name="revanced_alt_thumbnail_about_status_disabled">Showing original YouTube thumbnails</string>
<string name="revanced_alt_thumbnail_about_status_stills">Showing still video captures</string>
<string name="revanced_alt_thumbnail_about_status_dearrow">Showing DeArrow thumbnails. If a video has no DeArrow thumbnails then the original YouTube thumbnails are shown</string>
<string name="revanced_alt_thumbnail_about_status_dearrow_stills">Showing DeArrow thumbnails. If a video has no DeArrow thumbnails then still video captures are shown</string>
<string name="revanced_alt_thumbnail_dearrow_error">DeArrow temporarily not available (status code: %s)</string>
<string name="revanced_alt_thumbnail_dearrow_error_generic">DeArrow temporarily not available</string>
</resources>