mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-12 04:13:57 +01:00
Compare commits
18 Commits
v3.0.0-dev
...
v3.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2e0efd7ae | ||
|
|
a4a4822ed8 | ||
|
|
6b35561394 | ||
|
|
19f74bcdb1 | ||
|
|
dbe123c93c | ||
|
|
693d8a0f56 | ||
|
|
6a03c1f8c3 | ||
|
|
e820724111 | ||
|
|
ba58edbd7f | ||
|
|
a6d7c633f5 | ||
|
|
17f44ca780 | ||
|
|
3bd66406cc | ||
|
|
536b354fd6 | ||
|
|
e6a1573c59 | ||
|
|
d63288cc3e | ||
|
|
aaad91333f | ||
|
|
a98c178ca7 | ||
|
|
4672118e88 |
61
CHANGELOG.md
61
CHANGELOG.md
@@ -1,3 +1,64 @@
|
|||||||
|
# [3.0.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.8...v3.0.0-dev.9) (2023-12-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - GmsCore support:** Check for availability earlier to prevent crashing without any notice ([dab8900](https://github.com/ReVanced/revanced-patches/commit/dab8900e22498a86c7a1c2fd8f1bcc29dec1272c))
|
||||||
|
|
||||||
|
# [3.0.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.7...v3.0.0-dev.8) (2023-12-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Alternative Thumbnails:** Add option to use DeArrow ([#3378](https://github.com/ReVanced/revanced-patches/issues/3378)) ([41217f6](https://github.com/ReVanced/revanced-patches/commit/41217f61e600e47dd6812864bff22ee054521d3c))
|
||||||
|
|
||||||
|
# [3.0.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.6...v3.0.0-dev.7) (2023-12-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Twitter - Dynamic Color:** Remove blue icon and update app name ([#3384](https://github.com/ReVanced/revanced-patches/issues/3384)) ([3db6615](https://github.com/ReVanced/revanced-patches/commit/3db6615568e399aa13dac093868df3d0e1ebc4c3))
|
||||||
|
|
||||||
|
# [3.0.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.5...v3.0.0-dev.6) (2023-12-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Twitch - Settings:** Support version `16.1.0` and `15.4.1` ([#3377](https://github.com/ReVanced/revanced-patches/issues/3377)) ([062310d](https://github.com/ReVanced/revanced-patches/commit/062310dcc3923568c96171420c7fb9c0c2144233))
|
||||||
|
|
||||||
|
# [3.0.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.4...v3.0.0-dev.5) (2023-12-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Use correct class loader to load resources ([1d5f1f8](https://github.com/ReVanced/revanced-patches/commit/1d5f1f83be1f4eb78381887cd59f1649f1ed6d71))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Photomath:** Constrain patches to last working version ([f9a5dc6](https://github.com/ReVanced/revanced-patches/commit/f9a5dc6c91f37e9d7018e631739ca61511940d29))
|
||||||
|
|
||||||
|
# [3.0.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.3...v3.0.0-dev.4) (2023-12-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Minimized playback:** Fix PIP incorrectly shown for some Shorts playback ([#3364](https://github.com/ReVanced/revanced-patches/issues/3364)) ([84607ff](https://github.com/ReVanced/revanced-patches/commit/84607ff5f4bd30d328cdc4e1d46070a86d6c56bf))
|
||||||
|
|
||||||
|
# [3.0.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.2...v3.0.0-dev.3) (2023-12-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Return YouTube Dislike:** Prevent the first Short opened from freezing the UI ([#3359](https://github.com/ReVanced/revanced-patches/issues/3359)) ([e024409](https://github.com/ReVanced/revanced-patches/commit/e024409219bfbccc32c337d95da24b7146b6c7b7))
|
||||||
|
|
||||||
|
# [3.0.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.1...v3.0.0-dev.2) (2023-12-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube:** Clarify patch descriptions ([#3350](https://github.com/ReVanced/revanced-patches/issues/3350)) ([f2b9df4](https://github.com/ReVanced/revanced-patches/commit/f2b9df4e22a1c537cbd383087a3d724c3cdc1784))
|
||||||
|
|
||||||
# [3.0.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v2.203.0-dev.2...v3.0.0-dev.1) (2023-12-02)
|
# [3.0.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v2.203.0-dev.2...v3.0.0-dev.1) (2023-12-02)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -1422,6 +1423,10 @@ public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportResourceP
|
|||||||
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
|
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/misc/gms/fingerprints/HomeActivityFingerprint : app/revanced/patches/shared/integrations/AbstractIntegrationsPatch$IntegrationsFingerprint {
|
||||||
|
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/gms/fingerprints/HomeActivityFingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/integrations/AbstractIntegrationsPatch {
|
public final class app/revanced/patches/youtube/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/integrations/AbstractIntegrationsPatch {
|
||||||
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/integrations/IntegrationsPatch;
|
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/integrations/IntegrationsPatch;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
org.gradle.caching = true
|
org.gradle.caching = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 3.0.0-dev.1
|
version = 3.0.0-dev.9
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -4,6 +4,6 @@ rootProject.name = "revanced-patches"
|
|||||||
|
|
||||||
buildCache {
|
buildCache {
|
||||||
local {
|
local {
|
||||||
isEnabled = !System.getenv().containsKey("CI")
|
isEnabled = "CI" !in System.getenv()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import kotlin.random.Random
|
|||||||
name = "Spoof device ID",
|
name = "Spoof device ID",
|
||||||
description = "Spoofs device ID to mitigate manual bans by developers.",
|
description = "Spoofs device ID to mitigate manual bans by developers.",
|
||||||
dependencies = [SignatureDetectionPatch::class],
|
dependencies = [SignatureDetectionPatch::class],
|
||||||
compatiblePackages = [CompatiblePackage("com.microblink.photomath")]
|
compatiblePackages = [CompatiblePackage("com.microblink.photomath", ["8.32.0"])]
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object SpoofDeviceIdPatch : BytecodePatch(
|
object SpoofDeviceIdPatch : BytecodePatch(
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import app.revanced.patches.photomath.misc.unlockplus.fingerprints.IsPlusUnlocke
|
|||||||
@Patch(
|
@Patch(
|
||||||
name = "Unlock plus",
|
name = "Unlock plus",
|
||||||
dependencies = [SignatureDetectionPatch::class, EnableBookpointPatch::class],
|
dependencies = [SignatureDetectionPatch::class, EnableBookpointPatch::class],
|
||||||
compatiblePackages = [CompatiblePackage("com.microblink.photomath")]
|
compatiblePackages = [CompatiblePackage("com.microblink.photomath", ["8.32.0"])]
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object UnlockPlusPatch : BytecodePatch(
|
object UnlockPlusPatch : BytecodePatch(
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import java.io.Closeable
|
|||||||
description = "Adds settings menu to Twitch.",
|
description = "Adds settings menu to Twitch.",
|
||||||
dependencies = [IntegrationsPatch::class, SettingsResourcePatch::class],
|
dependencies = [IntegrationsPatch::class, SettingsResourcePatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage("tv.twitch.android.app", ["16.9.1"])
|
CompatiblePackage("tv.twitch.android.app", ["15.4.1", "16.1.0", "16.9.1"])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
object SettingsPatch : BytecodePatch(
|
object SettingsPatch : BytecodePatch(
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import java.nio.file.Files
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Dynamic color",
|
name = "Dynamic color",
|
||||||
description = "Replaces the default Twitter Blue with the user's Material You palette.",
|
description = "Replaces the default X (Formerly Twitter) Blue with the user's Material You palette.",
|
||||||
compatiblePackages = [CompatiblePackage("com.twitter.android")]
|
compatiblePackages = [CompatiblePackage("com.twitter.android")]
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@@ -46,8 +46,7 @@ object DynamicColorPatch : ResourcePatch() {
|
|||||||
"twitter_blue_opacity_30" to "@android:color/system_accent1_100",
|
"twitter_blue_opacity_30" to "@android:color/system_accent1_100",
|
||||||
"twitter_blue_opacity_50" to "@android:color/system_accent1_200",
|
"twitter_blue_opacity_50" to "@android:color/system_accent1_200",
|
||||||
"twitter_blue_opacity_58" to "@android:color/system_accent1_300",
|
"twitter_blue_opacity_58" to "@android:color/system_accent1_300",
|
||||||
"deep_transparent_twitter_blue" to "@android:color/system_accent1_200",
|
"deep_transparent_twitter_blue" to "@android:color/system_accent1_200"
|
||||||
"ic_launcher_background" to "#1DA1F2"
|
|
||||||
).forEach { (k, v) ->
|
).forEach { (k, v) ->
|
||||||
val colorElement = document.createElement("color")
|
val colorElement = document.createElement("color")
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Enable slide to seek",
|
name = "Enable slide to seek",
|
||||||
description = "Enable slide to seek instead of playing at 2x speed.",
|
description = "Enable slide to seek instead of playing at 2x speed when pressing and holding in the video player.",
|
||||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import java.nio.file.Files
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Custom branding",
|
name = "Custom branding",
|
||||||
description = "Changes the app icon and name to your choice (defaults to YouTube ReVanced and the ReVanced logo).",
|
description = "Changes the app name and icon to your choice (defaults to \"YouTube ReVanced\" and the ReVanced logo).",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage("com.google.android.youtube")
|
CompatiblePackage("com.google.android.youtube")
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Hide video action buttons",
|
name = "Hide video action buttons",
|
||||||
description = "Adds the options to hide action buttons under a video.",
|
description = "Adds options to hide action buttons under a video.",
|
||||||
dependencies = [
|
dependencies = [
|
||||||
ResourceMappingPatch::class,
|
ResourceMappingPatch::class,
|
||||||
LithoFilterPatch::class
|
LithoFilterPatch::class
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import com.android.tools.smali.dexlib2.Opcode
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Hide captions button",
|
name = "Hide captions button",
|
||||||
description = "Hides the captions button on video player.",
|
description = "Hides the captions button in the video player.",
|
||||||
dependencies = [
|
dependencies = [
|
||||||
IntegrationsPatch::class,
|
IntegrationsPatch::class,
|
||||||
SettingsPatch::class
|
SettingsPatch::class
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rc
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Hide player buttons",
|
name = "Hide player buttons",
|
||||||
description = "Adds the option to hide video player previous and next buttons.",
|
description = "Hides previous and next buttons in the video player.",
|
||||||
dependencies = [
|
dependencies = [
|
||||||
IntegrationsPatch::class,
|
IntegrationsPatch::class,
|
||||||
SettingsPatch::class
|
SettingsPatch::class
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Hide endscreen cards",
|
name = "Hide endscreen cards",
|
||||||
description = "Hides the suggested video cards at the end of a video in fullscreen.",
|
description = "Hides the suggested video cards at the end of videos.",
|
||||||
dependencies = [
|
dependencies = [
|
||||||
IntegrationsPatch::class,
|
IntegrationsPatch::class,
|
||||||
HideEndscreenCardsResourcePatch::class
|
HideEndscreenCardsResourcePatch::class
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Disable rolling number animations",
|
name = "Disable rolling number animations",
|
||||||
description = "Disables rolling number animations of video view count, upload time, and user likes",
|
description = "Disables rolling number animations of video view count, user likes, and upload time.",
|
||||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Disable player popup panels",
|
name = "Disable player popup panels",
|
||||||
description = "Disables panels from appearing automatically when going into fullscreen (playlist or live chat).",
|
description = "Disables panels (such as live chat) from opening automatically.",
|
||||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Custom player overlay opacity",
|
name = "Custom player overlay opacity",
|
||||||
description = "Change the opacity of the player background, when player controls are visible.",
|
description = "Change the opacity of the player background when player controls are visible.",
|
||||||
dependencies = [CustomPlayerOverlayOpacityResourcePatch::class],
|
dependencies = [CustomPlayerOverlayOpacityResourcePatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage("com.google.android.youtube")
|
CompatiblePackage("com.google.android.youtube")
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "SponsorBlock",
|
name = "SponsorBlock",
|
||||||
description = "Integrates SponsorBlock which allows skipping video segments such as sponsored content.",
|
description = "Integrates SponsorBlock, which can skip undesired video segments such as sponsored content.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
"com.google.android.youtube", [
|
"com.google.android.youtube", [
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|||||||
@Patch(
|
@Patch(
|
||||||
name = "Spoof app version",
|
name = "Spoof app version",
|
||||||
description = "Tricks YouTube into thinking you are running an older version of the app. " +
|
description = "Tricks YouTube into thinking you are running an older version of the app. " +
|
||||||
"One of the side effects also includes restoring the old UI.",
|
"This can be used to restore old UI elements and features.",
|
||||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;"
|
||||||
|
}
|
||||||
@@ -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;"),
|
||||||
@@ -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;"),
|
||||||
@@ -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;"),
|
||||||
@@ -112,7 +112,7 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
|
|
||||||
// Hook the player parameters.
|
// Hook the player parameters.
|
||||||
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameter(
|
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameter(
|
||||||
"$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;)Ljava/lang/String;"
|
"$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Z)Ljava/lang/String;"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Force the seekbar time and chapters to always show up.
|
// Force the seekbar time and chapters to always show up.
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import app.revanced.patches.youtube.misc.gms.Constants.REVANCED_YOUTUBE_PACKAGE_
|
|||||||
import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME
|
import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME
|
||||||
import app.revanced.patches.youtube.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorOption
|
import app.revanced.patches.youtube.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorOption
|
||||||
import app.revanced.patches.youtube.misc.gms.fingerprints.*
|
import app.revanced.patches.youtube.misc.gms.fingerprints.*
|
||||||
import app.revanced.patches.youtube.shared.fingerprints.WatchWhileActivityFingerprint
|
|
||||||
import app.revanced.util.exception
|
import app.revanced.util.exception
|
||||||
|
|
||||||
|
|
||||||
@@ -50,17 +49,17 @@ object GmsCoreSupportPatch : AbstractGmsCoreSupportPatch(
|
|||||||
CastDynamiteModuleV2Fingerprint,
|
CastDynamiteModuleV2Fingerprint,
|
||||||
CastContextFetchFingerprint,
|
CastContextFetchFingerprint,
|
||||||
PrimeMethodFingerprint,
|
PrimeMethodFingerprint,
|
||||||
WatchWhileActivityFingerprint
|
HomeActivityFingerprint,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
override val gmsCoreVendor by gmsCoreVendorOption
|
override val gmsCoreVendor by gmsCoreVendorOption
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
// Check the availability of GmsCore.
|
// Check the availability of GmsCore.
|
||||||
WatchWhileActivityFingerprint.result?.mutableMethod?.addInstruction(
|
HomeActivityFingerprint.result?.mutableMethod?.addInstruction(
|
||||||
0,
|
0,
|
||||||
"invoke-static {}, Lapp/revanced/integrations/patches/GmsCoreSupport;->checkAvailability()V"
|
"invoke-static {}, Lapp/revanced/integrations/patches/GmsCoreSupport;->checkAvailability()V"
|
||||||
) ?: throw WatchWhileActivityFingerprint.exception
|
) ?: throw HomeActivityFingerprint.exception
|
||||||
|
|
||||||
super.execute(context)
|
super.execute(context)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.gms.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patches.shared.integrations.AbstractIntegrationsPatch.IntegrationsFingerprint
|
||||||
|
|
||||||
|
object HomeActivityFingerprint : IntegrationsFingerprint(
|
||||||
|
customFingerprint = { methodDef, classDef ->
|
||||||
|
methodDef.name == "onCreate" && classDef.type.endsWith("Shell_HomeActivity;")
|
||||||
|
},
|
||||||
|
)
|
||||||
@@ -17,13 +17,19 @@ import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.Minimize
|
|||||||
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackSettingsParentFingerprint
|
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackSettingsParentFingerprint
|
||||||
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
|
import app.revanced.patches.youtube.video.information.VideoInformationPatch
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Minimized playback",
|
name = "Minimized playback",
|
||||||
description = "Enables minimized and background playback.",
|
description = "Enables minimized and background playback.",
|
||||||
dependencies = [IntegrationsPatch::class, PlayerTypeHookPatch::class, SettingsPatch::class],
|
dependencies = [
|
||||||
|
IntegrationsPatch::class,
|
||||||
|
PlayerTypeHookPatch::class,
|
||||||
|
VideoInformationPatch::class,
|
||||||
|
SettingsPatch::class
|
||||||
|
],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
"com.google.android.youtube",
|
"com.google.android.youtube",
|
||||||
@@ -65,7 +71,7 @@ object MinimizedPlaybackPatch : BytecodePatch(
|
|||||||
mutableMethod.addInstructions(
|
mutableMethod.addInstructions(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->isPlaybackNotShort()Z
|
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->playbackIsNotShort()Z
|
||||||
move-result v0
|
move-result v0
|
||||||
return v0
|
return v0
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Remove tracking query parameter",
|
name = "Remove tracking query parameter",
|
||||||
description = "Remove the tracking query parameter from links.",
|
description = "Remove the tracking query parameter from links you share.",
|
||||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||||
import app.revanced.patches.youtube.video.information.fingerprints.*
|
import app.revanced.patches.youtube.video.information.fingerprints.*
|
||||||
|
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
|
||||||
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
|
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
@@ -26,7 +27,7 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
description = "Hooks YouTube to get information about the current playing video.",
|
description = "Hooks YouTube to get information about the current playing video.",
|
||||||
dependencies = [IntegrationsPatch::class, VideoIdPatch::class]
|
dependencies = [IntegrationsPatch::class, VideoIdPatch::class, PlayerResponseMethodHookPatch::class]
|
||||||
)
|
)
|
||||||
object VideoInformationPatch : BytecodePatch(
|
object VideoInformationPatch : BytecodePatch(
|
||||||
setOf(
|
setOf(
|
||||||
@@ -115,6 +116,10 @@ object VideoInformationPatch : BytecodePatch(
|
|||||||
VideoIdPatch.hookBackgroundPlayVideoId(videoIdMethodDescriptor)
|
VideoIdPatch.hookBackgroundPlayVideoId(videoIdMethodDescriptor)
|
||||||
VideoIdPatch.hookPlayerResponseVideoId(
|
VideoIdPatch.hookPlayerResponseVideoId(
|
||||||
"$INTEGRATIONS_CLASS_DESCRIPTOR->setPlayerResponseVideoId(Ljava/lang/String;Z)V")
|
"$INTEGRATIONS_CLASS_DESCRIPTOR->setPlayerResponseVideoId(Ljava/lang/String;Z)V")
|
||||||
|
// Call before any other video id hooks,
|
||||||
|
// so they can use VideoInformation and check if the video id is for a Short.
|
||||||
|
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameterBeforeVideoId(
|
||||||
|
"$INTEGRATIONS_CLASS_DESCRIPTOR->newPlayerResponseSignature(Ljava/lang/String;Z)Ljava/lang/String;")
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the video time method
|
* Set the video time method
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ object PlayerResponseMethodHookPatch :
|
|||||||
Closeable,
|
Closeable,
|
||||||
MutableSet<PlayerResponseMethodHookPatch.Hook> by mutableSetOf() {
|
MutableSet<PlayerResponseMethodHookPatch.Hook> by mutableSetOf() {
|
||||||
private const val VIDEO_ID_PARAMETER = 1
|
private const val VIDEO_ID_PARAMETER = 1
|
||||||
private const val VIDEO_IS_OPENING_OR_PLAYING_PARAMETER = 11
|
private const val IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = 11
|
||||||
private const val PROTO_BUFFER_PARAMETER_PARAMETER = 3
|
private const val PROTO_BUFFER_PARAMETER_PARAMETER = 3
|
||||||
|
|
||||||
private lateinit var playerResponseMethod: MutableMethod
|
private lateinit var playerResponseMethod: MutableMethod
|
||||||
@@ -31,13 +31,13 @@ object PlayerResponseMethodHookPatch :
|
|||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
fun hookVideoId(hook: Hook) = playerResponseMethod.addInstruction(
|
fun hookVideoId(hook: Hook) = playerResponseMethod.addInstruction(
|
||||||
0, "invoke-static {p$VIDEO_ID_PARAMETER, p$VIDEO_IS_OPENING_OR_PLAYING_PARAMETER}, $hook"
|
0, "invoke-static {p$VIDEO_ID_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook"
|
||||||
)
|
)
|
||||||
|
|
||||||
fun hookProtoBufferParameter(hook: Hook) = playerResponseMethod.addInstructions(
|
fun hookProtoBufferParameter(hook: Hook) = playerResponseMethod.addInstructions(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
invoke-static {p$PROTO_BUFFER_PARAMETER_PARAMETER}, $hook
|
invoke-static {p$PROTO_BUFFER_PARAMETER_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook
|
||||||
move-result-object p$PROTO_BUFFER_PARAMETER_PARAMETER
|
move-result-object p$PROTO_BUFFER_PARAMETER_PARAMETER
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Remember video quality",
|
name = "Remember video quality",
|
||||||
description = "Adds the ability to remember the video quality you chose in the video quality flyout.",
|
description = "Adds the ability to remember the last video quality selected.",
|
||||||
dependencies = [IntegrationsPatch::class, VideoInformationPatch::class, SettingsPatch::class],
|
dependencies = [IntegrationsPatch::class, VideoInformationPatch::class, SettingsPatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ import app.revanced.patches.youtube.video.speed.remember.RememberPlaybackSpeedPa
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Playback speed",
|
name = "Playback speed",
|
||||||
description = "Adds custom playback speeds and ability " +
|
description = "Adds custom playback speeds and ability to remember the last playback speed selected.",
|
||||||
"to remember the playback speed you chose in the video playback speed flyout.",
|
|
||||||
dependencies = [CustomPlaybackSpeedPatch::class, RememberPlaybackSpeedPatch::class],
|
dependencies = [CustomPlaybackSpeedPatch::class, RememberPlaybackSpeedPatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import app.revanced.patches.youtube.video.speed.remember.fingerprint.InitializeP
|
|||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
description = "Adds the ability to remember the playback speed you chose in the playback speed flyout.",
|
|
||||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class, VideoInformationPatch::class, CustomPlaybackSpeedPatch::class]
|
dependencies = [IntegrationsPatch::class, SettingsPatch::class, VideoInformationPatch::class, CustomPlaybackSpeedPatch::class]
|
||||||
)
|
)
|
||||||
object RememberPlaybackSpeedPatch : BytecodePatch(
|
object RememberPlaybackSpeedPatch : BytecodePatch(
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ object VideoIdPatch : BytecodePatch(
|
|||||||
* Supports all videos and functions in all situations.
|
* Supports all videos and functions in all situations.
|
||||||
*
|
*
|
||||||
* First parameter is the video id.
|
* First parameter is the video id.
|
||||||
* Second parameter is if the video is being opened or is currently playing.
|
* Second parameter is if the video is a Short AND it is being opened or is currently playing.
|
||||||
*
|
*
|
||||||
* Hook is always called off the main thread.
|
* Hook is always called off the main thread.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import org.w3c.dom.Node
|
|||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
|
|
||||||
|
private val classLoader = object {}.javaClass.classLoader
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively traverse the DOM tree starting from the given root node.
|
* Recursively traverse the DOM tree starting from the given root node.
|
||||||
*
|
*
|
||||||
@@ -45,7 +47,6 @@ fun ResourceContext.mergeStrings(host: String) {
|
|||||||
* @param resources The resources to copy.
|
* @param resources The resources to copy.
|
||||||
*/
|
*/
|
||||||
fun ResourceContext.copyResources(sourceResourceDirectory: String, vararg resources: ResourceGroup) {
|
fun ResourceContext.copyResources(sourceResourceDirectory: String, vararg resources: ResourceGroup) {
|
||||||
val classLoader = javaClass.classLoader
|
|
||||||
val targetResourceDirectory = this["res"]
|
val targetResourceDirectory = this["res"]
|
||||||
|
|
||||||
for (resourceGroup in resources) {
|
for (resourceGroup in resources) {
|
||||||
@@ -77,7 +78,7 @@ fun ResourceContext.iterateXmlNodeChildren(
|
|||||||
targetTag: String,
|
targetTag: String,
|
||||||
callback: (node: Node) -> Unit
|
callback: (node: Node) -> Unit
|
||||||
) =
|
) =
|
||||||
xmlEditor[javaClass.classLoader.getResourceAsStream(resource)!!].use {
|
xmlEditor[classLoader.getResourceAsStream(resource)!!].use {
|
||||||
val stringsNode = it.file.getElementsByTagName(targetTag).item(0).childNodes
|
val stringsNode = it.file.getElementsByTagName(targetTag).item(0).childNodes
|
||||||
for (i in 1 until stringsNode.length - 1) callback(stringsNode.item(i))
|
for (i in 1 until stringsNode.length - 1) callback(stringsNode.item(i))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
<string name="revanced_ryd_compact_layout_summary_on">Like button styled for minimum width</string>
|
<string name="revanced_ryd_compact_layout_summary_on">Like button styled for minimum width</string>
|
||||||
<string name="revanced_ryd_compact_layout_summary_off">Like button styled for best appearance</string>
|
<string name="revanced_ryd_compact_layout_summary_off">Like button styled for best appearance</string>
|
||||||
|
|
||||||
<string name="ryd_toast_on_connection_error_title">Show toast if API not available</string>
|
<string name="ryd_toast_on_connection_error_title">Show toast if API is not available</string>
|
||||||
<string name="ryd_toast_on_connection_error_summary_on">Toast shown if ReturnYouTubeDislike API is not available</string>
|
<string name="ryd_toast_on_connection_error_summary_on">Toast shown if ReturnYouTubeDislike API is not available</string>
|
||||||
<string name="ryd_toast_on_connection_error_summary_off">Toast not shown if ReturnYouTubeDislike API is not available</string>
|
<string name="ryd_toast_on_connection_error_summary_off">Toast not shown if ReturnYouTubeDislike API is not available</string>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user