Compare commits

...

18 Commits

Author SHA1 Message Date
semantic-release-bot
3d986e6716 chore: Release v5.40.0-dev.9 [skip ci]
# [5.40.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.8...v5.40.0-dev.9) (2025-09-20)

### Features

* **YouTube Music:** Support version `8.10.52` ([#5941](https://github.com/ReVanced/revanced-patches/issues/5941)) ([01c0f1b](01c0f1bd1a))
2025-09-20 16:12:57 +00:00
LisoUseInAIKyrios
01c0f1bd1a feat(YouTube Music): Support version 8.10.52 (#5941) 2025-09-20 20:09:52 +04:00
github-actions[bot]
4178e8a64f chore: Sync translations (#5943) 2025-09-20 20:09:07 +04:00
semantic-release-bot
7e1bb8f3c7 chore: Release v5.40.0-dev.8 [skip ci]
# [5.40.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.7...v5.40.0-dev.8) (2025-09-20)

### Features

* **YouTube:** Support version `20.14.43` ([#5940](https://github.com/ReVanced/revanced-patches/issues/5940)) ([f7f4a1b](f7f4a1b0f0))
2025-09-20 15:33:42 +00:00
LisoUseInAIKyrios
f7f4a1b0f0 feat(YouTube): Support version 20.14.43 (#5940) 2025-09-20 19:30:05 +04:00
semantic-release-bot
e89660d234 chore: Release v5.40.0-dev.7 [skip ci]
# [5.40.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.6...v5.40.0-dev.7) (2025-09-20)

### Features

* **YouTube - Hide video action buttons:** Add "Hide comments" button ([db796fb](db796fb883))
2025-09-20 15:03:00 +00:00
LisoUseInAIKyrios
db796fb883 feat(YouTube - Hide video action buttons): Add "Hide comments" button
Button is only shown when using YouTube 20.14+ and the video information area is collapsed to a compact state
2025-09-20 19:00:00 +04:00
LisoUseInAIKyrios
6bb8bad8d7 chore(YouTube Music): Fix fingerprint typo, change hide cast button to default off 2025-09-20 18:03:41 +04:00
semantic-release-bot
aa1fb41ad8 chore: Release v5.40.0-dev.6 [skip ci]
# [5.40.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.5...v5.40.0-dev.6) (2025-09-20)

### Features

* **YouTube Music:** Add `Enable debugging` patch ([#5939](https://github.com/ReVanced/revanced-patches/issues/5939)) ([418f594](418f5945c2))
2025-09-20 12:37:33 +00:00
LisoUseInAIKyrios
418f5945c2 feat(YouTube Music): Add Enable debugging patch (#5939) 2025-09-20 16:33:03 +04:00
github-actions[bot]
e26c971067 chore: Sync translations (#5942) 2025-09-20 16:32:50 +04:00
semantic-release-bot
eb1d07fd98 chore: Release v5.40.0-dev.5 [skip ci]
# [5.40.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.4...v5.40.0-dev.5) (2025-09-20)

### Features

* **YouTube Music:** Add `Hide cast button` and `Navigation bar` patches ([#5934](https://github.com/ReVanced/revanced-patches/issues/5934)) ([651d358](651d358096))
2025-09-20 11:30:04 +00:00
MarcaD
651d358096 feat(YouTube Music): Add Hide cast button and Navigation bar patches (#5934) 2025-09-20 15:26:14 +04:00
semantic-release-bot
0d15c5f338 chore: Release v5.40.0-dev.4 [skip ci]
# [5.40.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.3...v5.40.0-dev.4) (2025-09-20)

### Bug Fixes

* **Spoof video streams:** Resolve occasional playback stuttering ([5c7c8b5](5c7c8b5364))
2025-09-20 10:39:29 +00:00
LisoUseInAIKyrios
5c7c8b5364 fix(Spoof video streams): Resolve occasional playback stuttering
Code adapted from:
2cf9db66ac
50d9c60374
2025-09-20 14:36:15 +04:00
semantic-release-bot
729997ec3e chore: Release v5.40.0-dev.3 [skip ci]
# [5.40.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.2...v5.40.0-dev.3) (2025-09-19)

### Bug Fixes

* **Instagram - Limit feed to followed profiles:** Change patch to default off ([767f1e3](767f1e3695))
2025-09-19 15:43:08 +00:00
LisoUseInAIKyrios
767f1e3695 fix(Instagram - Limit feed to followed profiles): Change patch to default off
Co-authored-by: brosssh <44944126+brosssh@users.noreply.github.com>
2025-09-19 19:40:32 +04:00
github-actions[bot]
7857876551 chore: Sync translations (#5933) 2025-09-19 19:40:03 +04:00
174 changed files with 3554 additions and 1069 deletions

View File

@@ -1,3 +1,52 @@
# [5.40.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.8...v5.40.0-dev.9) (2025-09-20)
### Features
* **YouTube Music:** Support version `8.10.52` ([#5941](https://github.com/ReVanced/revanced-patches/issues/5941)) ([01c0f1b](https://github.com/ReVanced/revanced-patches/commit/01c0f1bd1ac6edb8aea758f88ffffcdea74a29b7))
# [5.40.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.7...v5.40.0-dev.8) (2025-09-20)
### Features
* **YouTube:** Support version `20.14.43` ([#5940](https://github.com/ReVanced/revanced-patches/issues/5940)) ([f7f4a1b](https://github.com/ReVanced/revanced-patches/commit/f7f4a1b0f0186598266b41a2c6a781fdee49e440))
# [5.40.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.6...v5.40.0-dev.7) (2025-09-20)
### Features
* **YouTube - Hide video action buttons:** Add "Hide comments" button ([db796fb](https://github.com/ReVanced/revanced-patches/commit/db796fb8830b813e1ed626d491c4a797171e69e7))
# [5.40.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.5...v5.40.0-dev.6) (2025-09-20)
### Features
* **YouTube Music:** Add `Enable debugging` patch ([#5939](https://github.com/ReVanced/revanced-patches/issues/5939)) ([418f594](https://github.com/ReVanced/revanced-patches/commit/418f5945c213313f9a77cac9a5c326d89c754dfd))
# [5.40.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.4...v5.40.0-dev.5) (2025-09-20)
### Features
* **YouTube Music:** Add `Hide cast button` and `Navigation bar` patches ([#5934](https://github.com/ReVanced/revanced-patches/issues/5934)) ([651d358](https://github.com/ReVanced/revanced-patches/commit/651d3580967a252b57cbf4afbba02d6a4601ccfe))
# [5.40.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.3...v5.40.0-dev.4) (2025-09-20)
### Bug Fixes
* **Spoof video streams:** Resolve occasional playback stuttering ([5c7c8b5](https://github.com/ReVanced/revanced-patches/commit/5c7c8b536416ec53cd98f7d59d11850aa1b70f11))
# [5.40.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.2...v5.40.0-dev.3) (2025-09-19)
### Bug Fixes
* **Instagram - Limit feed to followed profiles:** Change patch to default off ([767f1e3](https://github.com/ReVanced/revanced-patches/commit/767f1e3695327bdbc4daea8b50a80d4c0a38456a))
# [5.40.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.1...v5.40.0-dev.2) (2025-09-18) # [5.40.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.40.0-dev.1...v5.40.0-dev.2) (2025-09-18)

View File

@@ -0,0 +1,24 @@
package app.revanced.extension.music.patches;
import static app.revanced.extension.shared.Utils.hideViewBy0dpUnderCondition;
import android.view.View;
import app.revanced.extension.music.settings.Settings;
@SuppressWarnings("unused")
public class HideCastButtonPatch {
/**
* Injection point
*/
public static int hideCastButton(int original) {
return Settings.HIDE_CAST_BUTTON.get() ? View.GONE : original;
}
/**
* Injection point
*/
public static void hideCastButton(View view) {
hideViewBy0dpUnderCondition(Settings.HIDE_CAST_BUTTON.get(), view);
}
}

View File

@@ -1,14 +0,0 @@
package app.revanced.extension.music.patches;
import app.revanced.extension.music.settings.Settings;
@SuppressWarnings("unused")
public class HideUpgradeButtonPatch {
/**
* Injection point
*/
public static boolean hideUpgradeButton() {
return Settings.HIDE_UPGRADE_BUTTON.get();
}
}

View File

@@ -0,0 +1,74 @@
package app.revanced.extension.music.patches;
import static app.revanced.extension.shared.Utils.hideViewUnderCondition;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.extension.music.settings.Settings;
@SuppressWarnings("unused")
public class NavigationBarPatch {
@NonNull
private static String lastYTNavigationEnumName = "";
public static void setLastAppNavigationEnum(@Nullable Enum<?> ytNavigationEnumName) {
if (ytNavigationEnumName != null) {
lastYTNavigationEnumName = ytNavigationEnumName.name();
}
}
public static void hideNavigationLabel(TextView textview) {
hideViewUnderCondition(Settings.HIDE_NAVIGATION_BAR_LABEL.get(), textview);
}
public static void hideNavigationButton(@NonNull View view) {
// Hide entire navigation bar.
if (Settings.HIDE_NAVIGATION_BAR.get() && view.getParent() != null) {
hideViewUnderCondition(true, (View) view.getParent());
return;
}
// Hide navigation buttons based on their type.
for (NavigationButton button : NavigationButton.values()) {
if (button.ytEnumNames.equals(lastYTNavigationEnumName)) {
hideViewUnderCondition(button.hidden, view);
break;
}
}
}
private enum NavigationButton {
HOME(
"TAB_HOME",
Settings.HIDE_NAVIGATION_BAR_HOME_BUTTON.get()
),
SAMPLES(
"TAB_SAMPLES",
Settings.HIDE_NAVIGATION_BAR_SAMPLES_BUTTON.get()
),
EXPLORE(
"TAB_EXPLORE",
Settings.HIDE_NAVIGATION_BAR_EXPLORE_BUTTON.get()
),
LIBRARY(
"LIBRARY_MUSIC",
Settings.HIDE_NAVIGATION_BAR_LIBRARY_BUTTON.get()
),
UPGRADE(
"TAB_MUSIC_PREMIUM",
Settings.HIDE_NAVIGATION_BAR_UPGRADE_BUTTON.get()
);
private final String ytEnumNames;
private final boolean hidden;
NavigationButton(@NonNull String ytEnumNames, boolean hidden) {
this.ytEnumNames = ytEnumNames;
this.hidden = hidden;
}
}
}

View File

@@ -15,10 +15,17 @@ public class Settings extends BaseSettings {
// Ads // Ads
public static final BooleanSetting HIDE_VIDEO_ADS = new BooleanSetting("revanced_music_hide_video_ads", TRUE, true); public static final BooleanSetting HIDE_VIDEO_ADS = new BooleanSetting("revanced_music_hide_video_ads", TRUE, true);
public static final BooleanSetting HIDE_GET_PREMIUM_LABEL = new BooleanSetting("revanced_music_hide_get_premium_label", TRUE, true); public static final BooleanSetting HIDE_GET_PREMIUM_LABEL = new BooleanSetting("revanced_music_hide_get_premium_label", TRUE, true);
public static final BooleanSetting HIDE_UPGRADE_BUTTON = new BooleanSetting("revanced_music_hide_upgrade_button", TRUE, true);
// General // General
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_music_hide_cast_button", TRUE, false);
public static final BooleanSetting HIDE_CATEGORY_BAR = new BooleanSetting("revanced_music_hide_category_bar", FALSE, true); public static final BooleanSetting HIDE_CATEGORY_BAR = new BooleanSetting("revanced_music_hide_category_bar", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR_HOME_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_home_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR_SAMPLES_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_samples_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR_EXPLORE_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_explore_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR_LIBRARY_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_library_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR_UPGRADE_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_upgrade_button", TRUE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR = new BooleanSetting("revanced_music_hide_navigation_bar", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR_LABEL = new BooleanSetting("revanced_music_hide_navigation_bar_labels", FALSE, true);
// Player // Player
public static final BooleanSetting PERMANENT_REPEAT = new BooleanSetting("revanced_music_play_permanent_repeat", FALSE, true); public static final BooleanSetting PERMANENT_REPEAT = new BooleanSetting("revanced_music_play_permanent_repeat", FALSE, true);

View File

@@ -116,7 +116,7 @@ public class Utils {
} }
/** /**
* @return The version name of the app, such as 19.11.43 * @return The version name of the app, such as 20.13.41
*/ */
public static String getAppVersionName() { public static String getAppVersionName() {
if (versionName == null) { if (versionName == null) {

View File

@@ -1,4 +1,4 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.shared.patches;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;

View File

@@ -97,6 +97,35 @@ public class SpoofVideoStreamsPatch {
return playerRequestUri; return playerRequestUri;
} }
/**
* Injection point.
*
* Blocks /get_watch requests by returning an unreachable URI.
* /att/get requests are used to obtain a PoToken challenge.
* See: <a href="https://github.com/FreeTubeApp/FreeTube/blob/4b7208430bc1032019a35a35eb7c8a84987ddbd7/src/botGuardScript.js#L15">botGuardScript.js#L15</a>
* <p>
* Since the Spoof streaming data patch was implemented because a valid PoToken cannot be obtained,
* Blocking /att/get requests are not a problem.
*/
public static String blockGetAttRequest(String originalUrlString) {
if (SPOOF_STREAMING_DATA) {
try {
var originalUri = Uri.parse(originalUrlString);
String path = originalUri.getPath();
if (path != null && path.contains("att/get")) {
Logger.printDebug(() -> "Blocking 'att/get' by returning internet connection check uri");
return INTERNET_CONNECTION_CHECK_URI_STRING;
}
} catch (Exception ex) {
Logger.printException(() -> "blockGetAttRequest failure", ex);
}
}
return originalUrlString;
}
/** /**
* Injection point. * Injection point.
* <p> * <p>
@@ -130,7 +159,7 @@ public class SpoofVideoStreamsPatch {
/** /**
* Injection point. * Injection point.
* Only invoked when playing a livestream on an iOS client. * Only invoked when playing a livestream on an Apple client.
*/ */
public static boolean fixHLSCurrentTime(boolean original) { public static boolean fixHLSCurrentTime(boolean original) {
if (!SPOOF_STREAMING_DATA) { if (!SPOOF_STREAMING_DATA) {
@@ -139,6 +168,14 @@ public class SpoofVideoStreamsPatch {
return false; return false;
} }
/*
* Injection point.
* Fix audio stuttering in YouTube Music.
*/
public static boolean disableSABR() {
return SPOOF_STREAMING_DATA;
}
/** /**
* Injection point. * Injection point.
* Turns off a feature flag that interferes with spoofing. * Turns off a feature flag that interferes with spoofing.

View File

@@ -7,6 +7,10 @@ final class ButtonsFilter extends Filter {
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.eml"; private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.eml";
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.eml"; private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.eml";
private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml"; private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml";
/**
* Video bar path when the video information is collapsed. Seems to shown only with 20.14+
*/
private static final String COMPACTIFY_VIDEO_ACTION_BAR_PATH = "compactify_video_action_bar.eml";
private static final String ANIMATED_VECTOR_TYPE_PATH = "AnimatedVectorType"; private static final String ANIMATED_VECTOR_TYPE_PATH = "AnimatedVectorType";
private final StringFilterGroup likeSubscribeGlow; private final StringFilterGroup likeSubscribeGlow;
@@ -82,6 +86,10 @@ final class ButtonsFilter extends Filter {
Settings.HIDE_STOP_ADS_BUTTON, Settings.HIDE_STOP_ADS_BUTTON,
"yt_outline_slash_circle_left" "yt_outline_slash_circle_left"
), ),
new ByteArrayFilterGroup(
Settings.HIDE_COMMENTS_BUTTON,
"yt_outline_message_bubble_right"
),
// Check for clip button both here and using a path filter, // Check for clip button both here and using a path filter,
// as there's a chance the path is a generic action button and won't contain 'clip_button' // as there's a chance the path is a generic action button and won't contain 'clip_button'
new ByteArrayFilterGroup( new ByteArrayFilterGroup(
@@ -124,9 +132,8 @@ final class ButtonsFilter extends Filter {
} }
if (matchedGroup == bufferFilterPathGroup) { if (matchedGroup == bufferFilterPathGroup) {
// Make sure the current path is the right one // Make sure the current path is the right one to avoid false positives.
// to avoid false positives. return (path.startsWith(VIDEO_ACTION_BAR_PATH) || path.startsWith(COMPACTIFY_VIDEO_ACTION_BAR_PATH))
return path.startsWith(VIDEO_ACTION_BAR_PATH)
&& bufferButtonsGroupList.check(buffer).isFiltered(); && bufferButtonsGroupList.check(buffer).isFiltered();
} }

View File

@@ -32,6 +32,7 @@ import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehavi
import android.graphics.Color; import android.graphics.Color;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting; import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.settings.EnumSetting; import app.revanced.extension.shared.settings.EnumSetting;
@@ -222,6 +223,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE); public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
public static final BooleanSetting HIDE_ASK_BUTTON = new BooleanSetting("revanced_hide_ask_button", FALSE); public static final BooleanSetting HIDE_ASK_BUTTON = new BooleanSetting("revanced_hide_ask_button", FALSE);
public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE); public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE);
public static final BooleanSetting HIDE_COMMENTS_BUTTON = new BooleanSetting("revanced_hide_comments_button", TRUE);
public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE); public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE);
public static final BooleanSetting HIDE_HYPE_BUTTON = new BooleanSetting("revanced_hide_hype_button", FALSE); public static final BooleanSetting HIDE_HYPE_BUTTON = new BooleanSetting("revanced_hide_hype_button", FALSE);
public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE); public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE);
@@ -513,10 +515,14 @@ public class Settings extends BaseSettings {
DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.resetToDefault(); DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.resetToDefault();
} }
// Old spoof versions that no longer work. // Old spoof versions that no longer work,
if (SPOOF_APP_VERSION_TARGET.get().compareTo(SPOOF_APP_VERSION_TARGET.defaultValue) < 0) { // or is spoofing to a version the same or newer than this app.
Logger.printInfo(() -> "Resetting spoof app version target"); if (!SPOOF_APP_VERSION_TARGET.isSetToDefault() &&
(SPOOF_APP_VERSION_TARGET.get().compareTo(SPOOF_APP_VERSION_TARGET.defaultValue) < 0
|| (Utils.getAppVersionName().compareTo(SPOOF_APP_VERSION_TARGET.get()) <= 0))) {
Logger.printInfo(() -> "Resetting spoof app version");
SPOOF_APP_VERSION_TARGET.resetToDefault(); SPOOF_APP_VERSION_TARGET.resetToDefault();
SPOOF_APP_VERSION.resetToDefault();
} }
// RYD requires manually migrating old settings since the lack of // RYD requires manually migrating old settings since the lack of

View File

@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
org.gradle.parallel = true org.gradle.parallel = true
android.useAndroidX = true android.useAndroidX = true
kotlin.code.style = official kotlin.code.style = official
version = 5.40.0-dev.2 version = 5.40.0-dev.9

View File

@@ -372,10 +372,18 @@ public final class app/revanced/patches/music/interaction/permanentshuffle/Perma
public static final fun getPermanentShufflePatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getPermanentShufflePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/music/layout/castbutton/HideCastButtonKt {
public static final fun getHideCastButton ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/layout/compactheader/HideCategoryBarKt { public final class app/revanced/patches/music/layout/compactheader/HideCategoryBarKt {
public static final fun getHideCategoryBar ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getHideCategoryBar ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/music/layout/navigationbar/NavigationBarPatchKt {
public static final fun getNavigationBarPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/layout/premium/HideGetPremiumPatchKt { public final class app/revanced/patches/music/layout/premium/HideGetPremiumPatchKt {
public static final fun getHideGetPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getHideGetPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
@@ -393,6 +401,10 @@ public final class app/revanced/patches/music/misc/backgroundplayback/Background
public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/music/misc/debugging/EnableDebuggingPatchKt {
public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/misc/extension/SharedExtensionPatchKt { public final class app/revanced/patches/music/misc/extension/SharedExtensionPatchKt {
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }

View File

@@ -15,6 +15,7 @@ internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/instagr
val limitFeedToFollowedProfiles = bytecodePatch( val limitFeedToFollowedProfiles = bytecodePatch(
name = "Limit feed to followed profiles", name = "Limit feed to followed profiles",
description = "Filters the home feed to display only content from profiles you follow.", description = "Filters the home feed to display only content from profiles you follow.",
use = false
) { ) {
compatibleWith("com.instagram.android") compatibleWith("com.instagram.android")

View File

@@ -24,7 +24,8 @@ val hideVideoAdsPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.apps.youtube.music"( "com.google.android.apps.youtube.music"(
"7.29.52" "7.29.52",
"8.10.52"
) )
) )

View File

@@ -17,7 +17,8 @@ val enableExclusiveAudioPlaybackPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.apps.youtube.music"( "com.google.android.apps.youtube.music"(
"7.29.52" "7.29.52",
"8.10.52"
) )
) )

View File

@@ -27,7 +27,8 @@ val permanentRepeatPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.apps.youtube.music"( "com.google.android.apps.youtube.music"(
"7.29.52" "7.29.52",
"8.10.52"
) )
) )

View File

@@ -11,7 +11,8 @@ val permanentShufflePatch = bytecodePatch(
) { ) {
compatibleWith( compatibleWith(
"com.google.android.apps.youtube.music"( "com.google.android.apps.youtube.music"(
"7.29.52" "7.29.52",
"8.10.52"
) )
) )

View File

@@ -0,0 +1,17 @@
package app.revanced.patches.music.layout.castbutton
import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
internal val mediaRouteButtonFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
returns("Z")
strings("MediaRouteButton")
}
internal val playerOverlayChipFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
literal { playerOverlayChip }
}

View File

@@ -0,0 +1,71 @@
package app.revanced.patches.music.layout.castbutton
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.settings.PreferenceScreen
import app.revanced.patches.music.misc.settings.settingsPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
internal var playerOverlayChip = -1L
private set
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideCastButtonPatch;"
@Suppress("unused")
val hideCastButton = bytecodePatch(
name = "Hide cast button",
description = "Adds an option to hide the cast button."
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
)
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.29.52",
"8.10.52"
)
)
execute {
addResources("music", "layout.castbutton.hideCastButton")
PreferenceScreen.GENERAL.addPreferences(
SwitchPreference("revanced_music_hide_cast_button"),
)
mediaRouteButtonFingerprint.classDef.apply {
val setVisibilityMethod = methods.first { method -> method.name == "setVisibility" }
setVisibilityMethod.addInstructions(
0,
"""
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->hideCastButton(I)I
move-result p1
"""
)
}
playerOverlayChipFingerprint.method.apply {
val resourceIndex = indexOfFirstLiteralInstructionOrThrow(playerOverlayChip)
val targetIndex = indexOfFirstInstructionOrThrow(resourceIndex, Opcode.MOVE_RESULT)
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstruction(
targetIndex + 1,
"invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideCastButton(Landroid/view/View;)V"
)
}
}
}

View File

@@ -1,6 +1,5 @@
package app.revanced.patches.music.layout.compactheader package app.revanced.patches.music.layout.compactheader
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
@@ -10,7 +9,6 @@ import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.settings.PreferenceScreen import app.revanced.patches.music.misc.settings.PreferenceScreen
import app.revanced.patches.music.misc.settings.settingsPatch import app.revanced.patches.music.misc.settings.settingsPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findFreeRegister import app.revanced.util.findFreeRegister
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@@ -29,7 +27,8 @@ val hideCategoryBar = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.apps.youtube.music"( "com.google.android.apps.youtube.music"(
"7.29.52" "7.29.52",
"8.10.52"
) )
) )

View File

@@ -0,0 +1,36 @@
package app.revanced.patches.music.layout.navigationbar
import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val tabLayoutTextFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("L")
opcodes(
Opcode.IGET,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT
)
strings("FEmusic_search")
custom { method, _ ->
method.containsLiteralInstruction(text1)
&& indexOfGetVisibilityInstruction(method) >= 0
}
}
internal fun indexOfGetVisibilityInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "getVisibility"
}

View File

@@ -0,0 +1,111 @@
package app.revanced.patches.music.layout.navigationbar
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.settings.PreferenceScreen
import app.revanced.patches.music.misc.settings.settingsPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
internal var text1 = -1L
private set
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/NavigationBarPatch;"
@Suppress("unused")
val navigationBarPatch = bytecodePatch(
name = "Navigation bar",
description = "Adds options to hide navigation bar, labels and buttons."
) {
dependsOn(
resourceMappingPatch,
sharedExtensionPatch,
settingsPatch,
addResourcesPatch
)
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.29.52",
"8.10.52"
)
)
execute {
text1 = resourceMappings[
"id",
"text1",
]
addResources("music", "layout.navigationbar.navigationBarPatch")
PreferenceScreen.GENERAL.addPreferences(
PreferenceScreenPreference(
key = "revanced_music_navigation_bar_screen",
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
preferences = setOf(
SwitchPreference("revanced_music_hide_navigation_bar_home_button"),
SwitchPreference("revanced_music_hide_navigation_bar_samples_button"),
SwitchPreference("revanced_music_hide_navigation_bar_explore_button"),
SwitchPreference("revanced_music_hide_navigation_bar_library_button"),
SwitchPreference("revanced_music_hide_navigation_bar_upgrade_button"),
SwitchPreference("revanced_music_hide_navigation_bar"),
SwitchPreference("revanced_music_hide_navigation_bar_labels"),
)
)
)
tabLayoutTextFingerprint.method.apply {
/**
* Hide navigation labels.
*/
val constIndex = indexOfFirstLiteralInstructionOrThrow(text1)
val targetIndex = indexOfFirstInstructionOrThrow(constIndex, Opcode.CHECK_CAST)
val targetParameter = getInstruction<ReferenceInstruction>(targetIndex).reference
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
if (!targetParameter.toString().endsWith("Landroid/widget/TextView;"))
throw PatchException("Method signature parameter did not match: $targetParameter")
addInstruction(
targetIndex + 1,
"invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideNavigationLabel(Landroid/widget/TextView;)V"
)
/**
* Set navigation enum and hide navigation buttons.
*/
val enumIndex = tabLayoutTextFingerprint.patternMatch!!.startIndex + 3
val enumRegister = getInstruction<OneRegisterInstruction>(enumIndex).registerA
val insertEnumIndex = indexOfFirstInstructionOrThrow(Opcode.AND_INT_LIT8) - 2
val pivotTabIndex = indexOfGetVisibilityInstruction(this)
val pivotTabRegister = getInstruction<FiveRegisterInstruction>(pivotTabIndex).registerC
addInstruction(
pivotTabIndex,
"invoke-static { v$pivotTabRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideNavigationButton(Landroid/view/View;)V"
)
addInstruction(
insertEnumIndex,
"invoke-static { v$enumRegister }, $EXTENSION_CLASS_DESCRIPTOR->setLastAppNavigationEnum(Ljava/lang/Enum;)V"
)
}
}
}

View File

@@ -28,7 +28,8 @@ val hideGetPremiumPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.apps.youtube.music"( "com.google.android.apps.youtube.music"(
"7.29.52" "7.29.52",
"8.10.52"
) )
) )

View File

@@ -1,18 +0,0 @@
package app.revanced.patches.music.layout.upgradebutton
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patcher.fingerprint
internal val pivotBarConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
returns("V")
parameters("L", "Z")
opcodes(
Opcode.CHECK_CAST,
Opcode.INVOKE_INTERFACE,
Opcode.GOTO,
Opcode.IPUT_OBJECT,
Opcode.RETURN_VOID
)
}

View File

@@ -1,104 +1,12 @@
package app.revanced.patches.music.layout.upgradebutton package app.revanced.patches.music.layout.upgradebutton
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.newLabel
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.toInstructions import app.revanced.patches.music.layout.navigationbar.navigationBarPatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.settings.PreferenceScreen
import app.revanced.patches.music.misc.settings.settingsPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction22t
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideUpgradeButtonPatch;"
@Deprecated("Patch is obsolete and was replaced by navigation bar patch", ReplaceWith("navigationBarPatch"))
@Suppress("unused") @Suppress("unused")
val hideUpgradeButton = bytecodePatch( val hideUpgradeButton = bytecodePatch{
name = "Hide upgrade button", dependsOn(navigationBarPatch)
description = "Hides the upgrade tab from the pivot bar.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
)
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.29.52"
)
)
execute {
addResources("music", "layout.upgradebutton.hideUpgradeButtonPatch")
// TODO: Add an extension patch to allow this to be enabled/disabled in app.
if (false) {
PreferenceScreen.ADS.addPreferences(
SwitchPreference("revanced_music_hide_upgrade_button")
)
}
pivotBarConstructorFingerprint.method.apply {
val pivotBarElementFieldReference =
getInstruction(pivotBarConstructorFingerprint.patternMatch!!.endIndex - 1)
.getReference<FieldReference>()
val register = getInstruction<FiveRegisterInstruction>(0).registerC
// First compile all the needed instructions.
val instructionList = """
invoke-interface { v0 }, Ljava/util/List;->size()I
move-result v1
const/4 v2, 0x4
invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
iput-object v0, v$register, $pivotBarElementFieldReference
""".toInstructions().toMutableList()
val endIndex = pivotBarConstructorFingerprint.patternMatch!!.endIndex
// Replace the instruction to retain the label at given index.
replaceInstruction(
endIndex - 1,
instructionList[0], // invoke-interface.
)
// Do not forget to remove this instruction since we added it already.
instructionList.removeFirst()
val exitInstruction = instructionList.last() // iput-object
addInstruction(
endIndex,
exitInstruction,
)
// Do not forget to remove this instruction since we added it already.
instructionList.removeLast()
// Add the necessary if statement to remove the upgrade tab button in case it exists.
instructionList.add(
2, // if-le.
BuilderInstruction22t(
Opcode.IF_LE,
1,
2,
newLabel(endIndex),
),
)
addInstructions(
endIndex,
instructionList,
)
}
}
} }
@Deprecated("Patch was renamed", ReplaceWith("hideUpgradeButton")) @Deprecated("Patch was renamed", ReplaceWith("hideUpgradeButton"))

View File

@@ -17,7 +17,8 @@ val bypassCertificateChecksPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.apps.youtube.music"( "com.google.android.apps.youtube.music"(
"7.29.52" "7.29.52",
"8.10.52"
) )
) )

View File

@@ -17,7 +17,8 @@ val backgroundPlaybackPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.apps.youtube.music"( "com.google.android.apps.youtube.music"(
"7.29.52" "7.29.52",
"8.10.52"
) )
) )

View File

@@ -0,0 +1,25 @@
package app.revanced.patches.music.misc.debugging
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.settings.PreferenceScreen
import app.revanced.patches.music.misc.settings.settingsPatch
import app.revanced.patches.shared.misc.debugging.enableDebuggingPatch
@Suppress("unused")
val enableDebuggingPatch = enableDebuggingPatch(
block = {
dependsOn(
sharedExtensionPatch,
settingsPatch,
)
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.29.52"
)
)
},
// String feature flag does not appear to be present with YT Music.
hookStringFeatureFlag = false,
preferenceScreen = PreferenceScreen.MISC
)

View File

@@ -8,12 +8,16 @@ import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.music.misc.extension.sharedExtensionPatch import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.* import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.settingsPatch import app.revanced.patches.shared.misc.settings.settingsPatch
import app.revanced.util.* import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
import app.revanced.util.copyXmlNode
import app.revanced.util.inputStreamFromBundledResource
import com.android.tools.smali.dexlib2.util.MethodUtil import com.android.tools.smali.dexlib2.util.MethodUtil
private const val BASE_ACTIVITY_HOOK_CLASS_DESCRIPTOR = private const val BASE_ACTIVITY_HOOK_CLASS_DESCRIPTOR =
@@ -23,7 +27,6 @@ private const val GOOGLE_API_ACTIVITY_HOOK_CLASS_DESCRIPTOR =
private val preferences = mutableSetOf<BasePreference>() private val preferences = mutableSetOf<BasePreference>()
private val settingsResourcePatch = resourcePatch { private val settingsResourcePatch = resourcePatch {
dependsOn( dependsOn(
resourceMappingPatch, resourceMappingPatch,
@@ -87,27 +90,6 @@ val settingsPatch = bytecodePatch(
addResources("music", "misc.settings.settingsPatch") addResources("music", "misc.settings.settingsPatch")
addResources("shared", "misc.debugging.enableDebuggingPatch") addResources("shared", "misc.debugging.enableDebuggingPatch")
// Should make a separate debugging patch, but for now include it with all installations.
PreferenceScreen.MISC.addPreferences(
PreferenceScreenPreference(
key = "revanced_debug_screen",
sorting = Sorting.UNSORTED,
preferences = setOf(
SwitchPreference("revanced_debug"),
NonInteractivePreference(
"revanced_debug_export_logs_to_clipboard",
tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference",
selectable = true
),
NonInteractivePreference(
"revanced_debug_logs_clear_buffer",
tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference",
selectable = true
)
)
)
)
// Add an "About" preference to the top. // Add an "About" preference to the top.
preferences += NonInteractivePreference( preferences += NonInteractivePreference(
key = "revanced_settings_music_screen_0_about", key = "revanced_settings_music_screen_0_about",
@@ -154,19 +136,19 @@ fun newIntent(settingsName: String) = IntentPreference.Intent(
object PreferenceScreen : BasePreferenceScreen() { object PreferenceScreen : BasePreferenceScreen() {
val ADS = Screen( val ADS = Screen(
"revanced_settings_music_screen_1_ads", key = "revanced_settings_music_screen_1_ads",
summaryKey = null summaryKey = null
) )
val GENERAL = Screen( val GENERAL = Screen(
"revanced_settings_music_screen_2_general", key = "revanced_settings_music_screen_2_general",
summaryKey = null summaryKey = null
) )
val PLAYER = Screen( val PLAYER = Screen(
"revanced_settings_music_screen_3_player", key = "revanced_settings_music_screen_3_player",
summaryKey = null summaryKey = null
) )
val MISC = Screen( val MISC = Screen(
"revanced_settings_music_screen_4_misc", key = "revanced_settings_music_screen_4_misc",
summaryKey = null summaryKey = null
) )

View File

@@ -33,7 +33,8 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch(
compatibleWith( compatibleWith(
"com.google.android.apps.youtube.music"( "com.google.android.apps.youtube.music"(
"7.29.52" "7.29.52",
"8.10.52"
) )
) )
}, },

View File

@@ -0,0 +1,147 @@
package app.revanced.patches.shared.misc.debugging
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatchBuilder
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/shared/patches/EnableDebuggingPatch;"
/**
* Patch shared with YouTube and YT Music.
*/
internal fun enableDebuggingPatch(
block: BytecodePatchBuilder.() -> Unit = {},
executeBlock: BytecodePatchContext.() -> Unit = {},
hookStringFeatureFlag: Boolean,
preferenceScreen: BasePreferenceScreen.Screen,
additionalDebugPreferences: List<BasePreference> = emptyList()
) = bytecodePatch(
name = "Enable debugging",
description = "Adds options for debugging and exporting ReVanced logs to the clipboard.",
) {
dependsOn(addResourcesPatch)
block()
execute {
executeBlock()
addResources("shared", "misc.debugging.enableDebuggingPatch")
val preferences = mutableSetOf<BasePreference>(
SwitchPreference("revanced_debug"),
)
preferences.addAll(additionalDebugPreferences)
preferences.addAll(
listOf(
SwitchPreference("revanced_debug_stacktrace"),
SwitchPreference("revanced_debug_toast_on_error"),
NonInteractivePreference(
"revanced_debug_export_logs_to_clipboard",
tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference",
selectable = true
),
NonInteractivePreference(
"revanced_debug_logs_clear_buffer",
tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference",
selectable = true
)
)
)
preferenceScreen.addPreferences(
PreferenceScreenPreference(
key = "revanced_debug_screen",
sorting = Sorting.UNSORTED,
preferences = preferences,
)
)
// Hook the methods that look up if a feature flag is active.
experimentalBooleanFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index,
"""
invoke-static { v$register, p1 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZLjava/lang/Long;)Z
move-result v$register
"""
)
}
}
experimentalDoubleFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
addInstructions(
insertIndex,
"""
move-result-wide v0 # Also clobbers v1 (p0) since result is wide.
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isDoubleFeatureFlagEnabled(DJD)D
move-result-wide v0
return-wide v0
"""
)
}
experimentalLongFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
addInstructions(
insertIndex,
"""
move-result-wide v0
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isLongFeatureFlagEnabled(JJJ)J
move-result-wide v0
return-wide v0
"""
)
}
if (hookStringFeatureFlag) experimentalStringFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
addInstructions(
insertIndex,
"""
move-result-object v0
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
move-result-object v0
return-object v0
"""
)
}
// There exists other experimental accessor methods for byte[]
// and wrappers for obfuscated classes, but currently none of those are hooked.
}
}

View File

@@ -0,0 +1,35 @@
package app.revanced.patches.shared.misc.debugging
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal val experimentalFeatureFlagParentFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("L")
parameters("L", "J", "[B")
strings("Unable to parse proto typed experiment flag: ")
}
internal val experimentalBooleanFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Z")
parameters("L", "J", "Z")
}
internal val experimentalDoubleFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("D")
parameters("J", "D")
}
internal val experimentalLongFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("J")
parameters("J", "J")
}
internal val experimentalStringFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Ljava/lang/String;")
parameters("J", "Ljava/lang/String;")
}

View File

@@ -6,6 +6,7 @@ import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.literal import app.revanced.util.literal
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
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val buildInitPlaybackRequestFingerprint = fingerprint { internal val buildInitPlaybackRequestFingerprint = fingerprint {
@@ -40,13 +41,6 @@ internal val buildRequestFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Lorg/chromium/net/UrlRequest") // UrlRequest; or UrlRequest$Builder; returns("Lorg/chromium/net/UrlRequest") // UrlRequest; or UrlRequest$Builder;
custom { methodDef, _ -> custom { methodDef, _ ->
if (methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.name == "newUrlRequestBuilder"
} < 0) {
return@custom false
}
// Different targets have slightly different parameters // Different targets have slightly different parameters
// Earlier targets have parameters: // Earlier targets have parameters:
@@ -80,10 +74,10 @@ internal val buildRequestFingerprint = fingerprint {
val parameterTypesSize = parameterTypes.size val parameterTypesSize = parameterTypes.size
(parameterTypesSize == 6 || parameterTypesSize == 7 || parameterTypesSize == 8) && (parameterTypesSize == 6 || parameterTypesSize == 7 || parameterTypesSize == 8) &&
parameterTypes[1] == "Ljava/util/Map;" // URL headers. parameterTypes[1] == "Ljava/util/Map;" // URL headers.
&& indexOfNewUrlRequestBuilderInstruction(methodDef) >= 0
} }
} }
internal val protobufClassParseByteBufferFingerprint = fingerprint { internal val protobufClassParseByteBufferFingerprint = fingerprint {
accessFlags(AccessFlags.PROTECTED, AccessFlags.STATIC) accessFlags(AccessFlags.PROTECTED, AccessFlags.STATIC)
returns("L") returns("L")
@@ -142,6 +136,17 @@ internal val hlsCurrentTimeFingerprint = fingerprint {
} }
} }
internal const val DISABLED_BY_SABR_STREAMING_URI_STRING = "DISABLED_BY_SABR_STREAMING_URI"
internal val mediaFetchEnumConstructorFingerprint = fingerprint {
returns("V")
strings(
"ENABLED",
"DISABLED_FOR_PLAYBACK",
DISABLED_BY_SABR_STREAMING_URI_STRING
)
}
internal val nerdsStatsVideoFormatBuilderFingerprint = fingerprint { internal val nerdsStatsVideoFormatBuilderFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Ljava/lang/String;") returns("Ljava/lang/String;")
@@ -186,3 +191,13 @@ internal val playbackStartDescriptorFeatureFlagFingerprint = fingerprint {
returns("Z") returns("Z")
literal { PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG } literal { PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG }
} }
internal fun indexOfNewUrlRequestBuilderInstruction(method: Method) = method.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_VIRTUAL && reference?.definingClass == "Lorg/chromium/net/CronetEngine;"
&& reference.name == "newUrlRequestBuilder"
&& reference.parameterTypes.size == 3
&& reference.parameterTypes[0] == "Ljava/lang/String;"
&& reference.parameterTypes[1] == "Lorg/chromium/net/UrlRequest\$Callback;"
&& reference.parameterTypes[2] == "Ljava/util/concurrent/Executor;"
}

View File

@@ -5,9 +5,11 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.fingerprint
import app.revanced.patcher.patch.BytecodePatchBuilder import app.revanced.patcher.patch.BytecodePatchBuilder
import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
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.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -22,15 +24,18 @@ import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
internal const val EXTENSION_CLASS_DESCRIPTOR = internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/shared/spoof/SpoofVideoStreamsPatch;" "Lapp/revanced/extension/shared/spoof/SpoofVideoStreamsPatch;"
private lateinit var buildRequestMethod: MutableMethod
private var buildRequestMethodUrlRegister = -1
fun spoofVideoStreamsPatch( fun spoofVideoStreamsPatch(
block: BytecodePatchBuilder.() -> Unit = {}, block: BytecodePatchBuilder.() -> Unit = {},
fixMediaFetchHotConfigChanges: BytecodePatchBuilder.() -> Boolean = { false }, fixMediaFetchHotConfigChanges: BytecodePatchBuilder.() -> Boolean = { false },
@@ -91,18 +96,17 @@ fun spoofVideoStreamsPatch(
// region Get replacement streams at player requests. // region Get replacement streams at player requests.
buildRequestFingerprint.method.apply { buildRequestFingerprint.method.apply {
val newRequestBuilderIndex = indexOfFirstInstructionOrThrow { buildRequestMethod = this
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "newUrlRequestBuilder" val newRequestBuilderIndex = indexOfNewUrlRequestBuilderInstruction(this)
} buildRequestMethodUrlRegister = getInstruction<FiveRegisterInstruction>(newRequestBuilderIndex).registerD
val urlRegister = getInstruction<FiveRegisterInstruction>(newRequestBuilderIndex).registerD val freeRegister = findFreeRegister(newRequestBuilderIndex, buildRequestMethodUrlRegister)
val freeRegister = findFreeRegister(newRequestBuilderIndex, urlRegister)
addInstructions( addInstructions(
newRequestBuilderIndex, newRequestBuilderIndex,
""" """
move-object v$freeRegister, p1 move-object v$freeRegister, p1
invoke-static { v$urlRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->fetchStreams(Ljava/lang/String;Ljava/util/Map;)V invoke-static { v$buildRequestMethodUrlRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->fetchStreams(Ljava/lang/String;Ljava/util/Map;)V
""" """
) )
} }
@@ -187,6 +191,21 @@ fun spoofVideoStreamsPatch(
// endregion // endregion
// region block getAtt request
buildRequestMethod.apply {
val insertIndex = indexOfNewUrlRequestBuilderInstruction(this)
addInstructions(
insertIndex, """
invoke-static { v$buildRequestMethodUrlRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetAttRequest(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$buildRequestMethodUrlRegister
"""
)
}
// endregion
// region Remove /videoplayback request body to fix playback. // region Remove /videoplayback request body to fix playback.
// It is assumed, YouTube makes a request with a body tuned for Android. // It is assumed, YouTube makes a request with a body tuned for Android.
// Requesting streams intended for other platforms with a body tuned for Android could be the cause of 400 errors. // Requesting streams intended for other platforms with a body tuned for Android could be the cause of 400 errors.
@@ -243,6 +262,50 @@ fun spoofVideoStreamsPatch(
// endregion // endregion
// region Disable SABR playback.
// If SABR is disabled, it seems 'MediaFetchHotConfig' may no longer need an override (not confirmed).
val (mediaFetchEnumClass, sabrFieldReference) = with(mediaFetchEnumConstructorFingerprint.method) {
val stringIndex = mediaFetchEnumConstructorFingerprint.stringMatches!!.first {
it.string == DISABLED_BY_SABR_STREAMING_URI_STRING
}.index
val mediaFetchEnumClass = definingClass
val sabrFieldIndex = indexOfFirstInstructionOrThrow(stringIndex) {
opcode == Opcode.SPUT_OBJECT &&
getReference<FieldReference>()?.type == mediaFetchEnumClass
}
Pair(
mediaFetchEnumClass,
getInstruction<ReferenceInstruction>(sabrFieldIndex).reference
)
}
fingerprint {
returns(mediaFetchEnumClass)
opcodes(
Opcode.SGET_OBJECT,
Opcode.RETURN_OBJECT,
)
custom { method, _ ->
!method.parameterTypes.isEmpty()
}
}.method.addInstructionsWithLabels(
0,
"""
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disableSABR()Z
move-result v0
if-eqz v0, :ignore
sget-object v0, $sabrFieldReference
return-object v0
:ignore
nop
"""
)
// endregion
// region turn off stream config replacement feature flag. // region turn off stream config replacement feature flag.
if (fixMediaFetchHotConfigChanges()) { if (fixMediaFetchHotConfigChanges()) {

View File

@@ -78,10 +78,9 @@ val hideAdsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -27,10 +27,9 @@ val hideGetPremiumPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -25,10 +25,9 @@ val videoAdsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -55,10 +55,9 @@ val copyVideoUrlPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -26,10 +26,9 @@ val removeViewerDiscretionDialogPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -29,8 +29,8 @@ val disableDoubleTapActionsPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -76,10 +76,9 @@ val downloadsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -22,10 +22,9 @@ val seekbarPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )
} }

View File

@@ -90,10 +90,9 @@ val swipeControlsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -26,10 +26,9 @@ val autoCaptionsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -45,10 +45,9 @@ val customBrandingPatch = resourcePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -95,10 +95,9 @@ val changeHeaderPatch = resourcePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -24,10 +24,9 @@ val hideButtonsPatch = resourcePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )
@@ -41,6 +40,7 @@ val hideButtonsPatch = resourcePatch(
SwitchPreference("revanced_disable_like_subscribe_glow"), SwitchPreference("revanced_disable_like_subscribe_glow"),
SwitchPreference("revanced_hide_ask_button"), SwitchPreference("revanced_hide_ask_button"),
SwitchPreference("revanced_hide_clip_button"), SwitchPreference("revanced_hide_clip_button"),
SwitchPreference("revanced_hide_comments_button"),
SwitchPreference("revanced_hide_download_button"), SwitchPreference("revanced_hide_download_button"),
SwitchPreference("revanced_hide_hype_button"), SwitchPreference("revanced_hide_hype_button"),
SwitchPreference("revanced_hide_like_dislike_button"), SwitchPreference("revanced_hide_like_dislike_button"),

View File

@@ -42,10 +42,9 @@ val navigationButtonsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -60,10 +60,9 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -35,10 +35,9 @@ val changeFormFactorPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -61,10 +61,9 @@ val hideEndscreenCardsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -33,10 +33,9 @@ val hideEndScreenSuggestedVideoPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -31,10 +31,9 @@ val disableFullscreenAmbientModePatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -128,10 +128,9 @@ val hideLayoutComponentsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -59,10 +59,9 @@ val hideInfoCardsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -26,10 +26,9 @@ val hidePlayerFlyoutMenuPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -50,10 +50,9 @@ val hideRelatedVideoOverlayPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -31,10 +31,9 @@ val disableRollingNumberAnimationPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -174,10 +174,9 @@ val hideShortsComponentsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -33,10 +33,9 @@ val disableSignInToTvPopupPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -23,10 +23,9 @@ val hideTimestampPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -155,10 +155,9 @@ val miniplayerPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -23,10 +23,9 @@ val playerPopupPanelsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -23,10 +23,9 @@ internal val exitFullscreenPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -25,10 +25,9 @@ val openVideosFullscreenPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -54,10 +54,9 @@ val customPlayerOverlayOpacityPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -63,10 +63,9 @@ val returnYouTubeDislikePatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -68,10 +68,9 @@ val wideSearchbarPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -46,10 +46,9 @@ val shortsAutoplayPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -66,10 +66,9 @@ val openShortsInRegularPlayerPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -128,10 +128,9 @@ val sponsorBlockPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -17,6 +17,7 @@ import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPref
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_43_or_greater import app.revanced.patches.youtube.misc.playservice.is_19_43_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_14_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
@@ -60,10 +61,9 @@ val spoofAppVersionPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )
@@ -79,17 +79,21 @@ val spoofAppVersionPatch = bytecodePatch(
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory", tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
preferences = setOf( preferences = setOf(
SwitchPreference("revanced_spoof_app_version"), SwitchPreference("revanced_spoof_app_version"),
if (is_19_43_or_greater) { if (is_20_14_or_greater) {
ListPreference("revanced_spoof_app_version_target")
} else if (is_19_43_or_greater) {
ListPreference( ListPreference(
key = "revanced_spoof_app_version_target", key = "revanced_spoof_app_version_target",
summaryKey = null summaryKey = null,
entriesKey = "revanced_spoof_app_version_target_legacy_20_13_entries",
entryValuesKey = "revanced_spoof_app_version_target_legacy_20_13_entry_values"
) )
} else { } else {
ListPreference( ListPreference(
key = "revanced_spoof_app_version_target", key = "revanced_spoof_app_version_target",
summaryKey = null, summaryKey = null,
entriesKey = "revanced_spoof_app_version_target_legacy_entries", entriesKey = "revanced_spoof_app_version_target_legacy_19_34_entries",
entryValuesKey = "revanced_spoof_app_version_target_legacy_entry_values" entryValuesKey = "revanced_spoof_app_version_target_legacy_19_34_entry_values"
) )
} }
) )

View File

@@ -34,10 +34,9 @@ val changeStartPagePatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -36,10 +36,9 @@ val disableResumingShortsOnStartupPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -206,10 +206,9 @@ val themePatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -35,10 +35,9 @@ val alternativeThumbnailsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -29,10 +29,9 @@ val bypassImageRegionRestrictionsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -25,10 +25,9 @@ val announcementsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -26,10 +26,9 @@ val autoRepeatPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -53,10 +53,9 @@ val backgroundPlaybackPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -1,144 +1,34 @@
package app.revanced.patches.youtube.misc.debugging package app.revanced.patches.youtube.misc.debugging
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.debugging.enableDebuggingPatch
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR = @Suppress("unused")
"Lapp/revanced/extension/youtube/patches/EnableDebuggingPatch;" val enableDebuggingPatch = enableDebuggingPatch(
block = {
// TODO: Refactor this into a shared patch that can be used by both YT and YT Music. dependsOn(
// Almost all of the feature flag hooks are the same between both apps. sharedExtensionPatch,
val enableDebuggingPatch = bytecodePatch( settingsPatch,
name = "Enable debugging",
description = "Adds options for debugging and exporting ReVanced logs to the clipboard.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
versionCheckPatch
)
compatibleWith(
"com.google.android.youtube"(
"19.34.42",
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
"20.13.41",
) )
)
execute { compatibleWith(
addResources("shared", "misc.debugging.enableDebuggingPatch") "com.google.android.youtube"(
"19.34.42",
"19.43.41",
"20.07.39",
"20.13.41",
"20.14.43",
)
)
},
executeBlock = {
addResources("youtube", "misc.debugging.enableDebuggingPatch") addResources("youtube", "misc.debugging.enableDebuggingPatch")
},
PreferenceScreen.MISC.addPreferences( hookStringFeatureFlag = true,
PreferenceScreenPreference( preferenceScreen = PreferenceScreen.MISC,
key = "revanced_debug_screen", additionalDebugPreferences = listOf(SwitchPreference("revanced_debug_protobuffer"))
sorting = Sorting.UNSORTED, )
preferences = setOf(
SwitchPreference("revanced_debug"),
SwitchPreference("revanced_debug_protobuffer"),
SwitchPreference("revanced_debug_stacktrace"),
SwitchPreference("revanced_debug_toast_on_error"),
NonInteractivePreference(
"revanced_debug_export_logs_to_clipboard",
tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference",
selectable = true
),
NonInteractivePreference(
"revanced_debug_logs_clear_buffer",
tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference",
selectable = true
),
),
),
)
// Hook the methods that look up if a feature flag is active.
experimentalBooleanFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index,
"""
invoke-static { v$register, p1 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZLjava/lang/Long;)Z
move-result v$register
"""
)
}
}
experimentalDoubleFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
addInstructions(
insertIndex,
"""
move-result-wide v0 # Also clobbers v1 (p0) since result is wide.
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isDoubleFeatureFlagEnabled(DJD)D
move-result-wide v0
return-wide v0
"""
)
}
experimentalLongFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
addInstructions(
insertIndex,
"""
move-result-wide v0
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isLongFeatureFlagEnabled(JJJ)J
move-result-wide v0
return-wide v0
"""
)
}
experimentalStringFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
addInstructions(
insertIndex,
"""
move-result-object v0
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
move-result-object v0
return-object v0
"""
)
}
// There exists other experimental accessor methods for byte[]
// and wrappers for obfuscated classes, but currently none of those are hooked.
}
}

View File

@@ -26,10 +26,9 @@ val spoofDeviceDimensionsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -23,10 +23,9 @@ val checkWatchHistoryDomainNameResolutionPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -1,8 +1,8 @@
package app.revanced.patches.youtube.misc.fix.backtoexitgesture package app.revanced.patches.youtube.misc.fix.backtoexitgesture
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val scrollPositionFingerprint = fingerprint { internal val scrollPositionFingerprint = fingerprint {
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL) accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
@@ -24,30 +24,18 @@ internal val recyclerViewTopScrollingFingerprint = fingerprint {
returns("V") returns("V")
parameters() parameters()
opcodes( opcodes(
Opcode.IGET_OBJECT,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST, Opcode.CHECK_CAST,
Opcode.CONST_4, Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_VIRTUAL,
Opcode.GOTO, Opcode.GOTO,
Opcode.IGET_OBJECT, Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE, Opcode.INVOKE_INTERFACE
) )
} }
internal val recyclerViewTopScrollingParentFingerprint = fingerprint { internal val recyclerViewTopScrollingParentFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters("L", "L", "Landroid/view/ViewGroup;", "Landroid/view/ViewGroup;")
opcodes( opcodes(
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT,
@@ -55,5 +43,8 @@ internal val recyclerViewTopScrollingParentFingerprint = fingerprint {
Opcode.CONST_16, Opcode.CONST_16,
Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_VIRTUAL,
Opcode.NEW_INSTANCE, Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.RETURN_VOID
) )
} }

View File

@@ -15,13 +15,14 @@ internal val fixBackToExitGesturePatch = bytecodePatch(
) { ) {
execute { execute {
recyclerViewTopScrollingFingerprint.match(recyclerViewTopScrollingParentFingerprint.originalClassDef) recyclerViewTopScrollingFingerprint.match(
.let { recyclerViewTopScrollingParentFingerprint.originalClassDef
it.method.addInstruction( ).let {
it.patternMatch!!.endIndex, it.method.addInstruction(
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onTopView()V" it.patternMatch!!.endIndex,
) "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onTopView()V"
} )
}
scrollPositionFingerprint.let { scrollPositionFingerprint.let {
navigate(it.originalMethod) navigate(it.originalMethod)

View File

@@ -37,10 +37,9 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
YOUTUBE_PACKAGE_NAME( YOUTUBE_PACKAGE_NAME(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )
} }

View File

@@ -28,10 +28,9 @@ val disableHapticFeedbackPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -34,10 +34,9 @@ val bypassURLRedirectsPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -43,10 +43,9 @@ val openLinksExternallyPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -32,10 +32,9 @@ val removeTrackingQueryParameterPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -24,10 +24,9 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -46,10 +46,9 @@ val forceOriginalAudioPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -56,10 +56,9 @@ val disableHdrPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -26,10 +26,9 @@ val videoQualityPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -30,10 +30,9 @@ val playbackSpeedPatch = bytecodePatch(
"com.google.android.youtube"( "com.google.android.youtube"(
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
"20.13.41", "20.13.41",
"20.14.43",
) )
) )

View File

@@ -89,6 +89,7 @@ Second \"item\" text"</string>
<patch id="layout.buttons.action.hideButtonsPatch"> <patch id="layout.buttons.action.hideButtonsPatch">
<!-- 'Share' should be translated with the same localized wording that YouTube displays. --> <!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Stop ads' should be translated with the same localized wording that YouTube displays. --> <!-- 'Stop ads' should be translated with the same localized wording that YouTube displays. -->
<!-- Button does not have any text and is only shown as an icon, and only when the video information area is collapsed to a compact state. -->
<!-- 'Report' should be translated with the same localized wording that YouTube displays. <!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually only shows on live streams. --> This button usually only shows on live streams. -->
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. --> <!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
@@ -253,8 +254,17 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="interaction.permanentrepeat.permanentRepeatPatch"> <patch id="interaction.permanentrepeat.permanentRepeatPatch">
</patch> </patch>
<patch id="layout.castbutton.hideCastButton">
</patch>
<patch id="layout.compactheader.hideCategoryBar"> <patch id="layout.compactheader.hideCategoryBar">
</patch> </patch>
<patch id="layout.navigationbar.navigationBarPatch">
<!-- 'Home' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Samples' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Explore' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Library' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Upgrade' should be translated using the same localized wording YouTube Music displays for the tab. -->
</patch>
<patch id="layout.premium.hideGetPremiumPatch"> <patch id="layout.premium.hideGetPremiumPatch">
</patch> </patch>
<patch id="layout.upgradebutton.hideUpgradeButtonPatch"> <patch id="layout.upgradebutton.hideUpgradeButtonPatch">

View File

@@ -89,6 +89,7 @@ Second \"item\" text"</string>
<patch id="layout.buttons.action.hideButtonsPatch"> <patch id="layout.buttons.action.hideButtonsPatch">
<!-- 'Share' should be translated with the same localized wording that YouTube displays. --> <!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Stop ads' should be translated with the same localized wording that YouTube displays. --> <!-- 'Stop ads' should be translated with the same localized wording that YouTube displays. -->
<!-- Button does not have any text and is only shown as an icon, and only when the video information area is collapsed to a compact state. -->
<!-- 'Report' should be translated with the same localized wording that YouTube displays. <!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually only shows on live streams. --> This button usually only shows on live streams. -->
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. --> <!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
@@ -253,8 +254,17 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="interaction.permanentrepeat.permanentRepeatPatch"> <patch id="interaction.permanentrepeat.permanentRepeatPatch">
</patch> </patch>
<patch id="layout.castbutton.hideCastButton">
</patch>
<patch id="layout.compactheader.hideCategoryBar"> <patch id="layout.compactheader.hideCategoryBar">
</patch> </patch>
<patch id="layout.navigationbar.navigationBarPatch">
<!-- 'Home' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Samples' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Explore' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Library' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Upgrade' should be translated using the same localized wording YouTube Music displays for the tab. -->
</patch>
<patch id="layout.premium.hideGetPremiumPatch"> <patch id="layout.premium.hideGetPremiumPatch">
</patch> </patch>
<patch id="layout.upgradebutton.hideUpgradeButtonPatch"> <patch id="layout.upgradebutton.hideUpgradeButtonPatch">

View File

@@ -107,6 +107,15 @@ Second \"item\" text"</string>
<string name="revanced_debug_title">تسجيل تصحيح الأخطاء</string> <string name="revanced_debug_title">تسجيل تصحيح الأخطاء</string>
<string name="revanced_debug_summary_on">تم تمكين تسجيلات تصحيح الأخطاء</string> <string name="revanced_debug_summary_on">تم تمكين تسجيلات تصحيح الأخطاء</string>
<string name="revanced_debug_summary_off">تم تعطيل تسجيلات تصحيح الأخطاء</string> <string name="revanced_debug_summary_off">تم تعطيل تسجيلات تصحيح الأخطاء</string>
<string name="revanced_debug_stacktrace_title">سجل تتبع المكدس</string>
<string name="revanced_debug_stacktrace_summary_on">تتضمن سجلات التصحيح سجل تتبع المكدس</string>
<string name="revanced_debug_stacktrace_summary_off">لا تتضمن سجلات التصحيح سجل تتبع المكدس</string>
<string name="revanced_debug_toast_on_error_title">عرض ملاحظة عند وجود خطأ في ReVanced</string>
<string name="revanced_debug_toast_on_error_summary_on">يتم عرض ملاحظة في حالة حدوث خطأ</string>
<string name="revanced_debug_toast_on_error_summary_off">لا يتم عرض ملاحظة في حالة حدوث خطأ</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">"يؤدي إيقاف تشغيل ملاحظات الأخطاء إلى إخفاء كافة إشعارات أخطاء ReVanced.
لن يتم إعلامك بأي أخطاء غير متوقعة."</string>
<string name="revanced_debug_export_logs_to_clipboard_title">تصدير سجلات تصحيح الأخطاء</string> <string name="revanced_debug_export_logs_to_clipboard_title">تصدير سجلات تصحيح الأخطاء</string>
<string name="revanced_debug_export_logs_to_clipboard_summary">نسخ سجلات تصحيح أخطاء ReVanced إلى الحافظة</string> <string name="revanced_debug_export_logs_to_clipboard_summary">نسخ سجلات تصحيح أخطاء ReVanced إلى الحافظة</string>
<string name="revanced_debug_logs_disabled">تم تعطيل تسجيلات تصحيح الأخطاء</string> <string name="revanced_debug_logs_disabled">تم تعطيل تسجيلات تصحيح الأخطاء</string>
@@ -151,15 +160,6 @@ Second \"item\" text"</string>
يمكن أن يساعد هذا في تحديد المكونات عند إنشاء عوامل تصفية مخصصة. يمكن أن يساعد هذا في تحديد المكونات عند إنشاء عوامل تصفية مخصصة.
ومع ذلك، سيؤدي تمكين هذا أيضًا إلى تسجيل بعض بيانات المستخدم مثل عنوان IP الخاص بك."</string> ومع ذلك، سيؤدي تمكين هذا أيضًا إلى تسجيل بعض بيانات المستخدم مثل عنوان IP الخاص بك."</string>
<string name="revanced_debug_stacktrace_title">سجل تتبع المكدس</string>
<string name="revanced_debug_stacktrace_summary_on">تتضمن سجلات التصحيح سجل تتبع المكدس</string>
<string name="revanced_debug_stacktrace_summary_off">لا تتضمن سجلات التصحيح سجل تتبع المكدس</string>
<string name="revanced_debug_toast_on_error_title">عرض ملاحظة عند وجود خطأ في ReVanced</string>
<string name="revanced_debug_toast_on_error_summary_on">يتم عرض ملاحظة في حالة حدوث خطأ</string>
<string name="revanced_debug_toast_on_error_summary_off">لا يتم عرض ملاحظة في حالة حدوث خطأ</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">"يؤدي إيقاف تشغيل ملاحظات الأخطاء إلى إخفاء كافة إشعارات أخطاء ReVanced.
لن يتم إعلامك بأي أخطاء غير متوقعة."</string>
</patch> </patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch"> <patch id="layout.hide.general.hideLayoutComponentsPatch">
<string name="revanced_hide_album_cards_title">إخفاء بطاقات الألبوم</string> <string name="revanced_hide_album_cards_title">إخفاء بطاقات الألبوم</string>
@@ -586,6 +586,10 @@ Second \"item\" text"</string>
<string name="revanced_hide_stop_ads_button_title">إخفاء إيقاف الإعلانات</string> <string name="revanced_hide_stop_ads_button_title">إخفاء إيقاف الإعلانات</string>
<string name="revanced_hide_stop_ads_button_summary_on">زر إيقاف الإعلانات مخفي</string> <string name="revanced_hide_stop_ads_button_summary_on">زر إيقاف الإعلانات مخفي</string>
<string name="revanced_hide_stop_ads_button_summary_off">زر إيقاف الإعلانات معروض</string> <string name="revanced_hide_stop_ads_button_summary_off">زر إيقاف الإعلانات معروض</string>
<!-- Button does not have any text and is only shown as an icon, and only when the video information area is collapsed to a compact state. -->
<string name="revanced_hide_comments_button_title">إخفاء التعليقات</string>
<string name="revanced_hide_comments_button_summary_on">زر التعليقات مخفي</string>
<string name="revanced_hide_comments_button_summary_off">زر التعليقات ظاهر</string>
<!-- 'Report' should be translated with the same localized wording that YouTube displays. <!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually only shows on live streams. --> This button usually only shows on live streams. -->
<string name="revanced_hide_report_button_title">إخفاء الإبلاغ</string> <string name="revanced_hide_report_button_title">إخفاء الإبلاغ</string>
@@ -1242,8 +1246,9 @@ Second \"item\" text"</string>
إذا تم إيقاف تشغيله لاحقًا، من المستحسن مسح بيانات التطبيق لمنع حدوث أخطاء في واجهة المستخدم."</string> إذا تم إيقاف تشغيله لاحقًا، من المستحسن مسح بيانات التطبيق لمنع حدوث أخطاء في واجهة المستخدم."</string>
<string name="revanced_spoof_app_version_target_title">الهدف من تغيير إصدار التطبيق</string> <string name="revanced_spoof_app_version_target_title">الهدف من تغيير إصدار التطبيق</string>
<string name="revanced_spoof_app_version_target_entry_1">19.35.36 - استعادة أيقونات مشغل Shorts القديمة</string> <string name="revanced_spoof_app_version_target_entry_1">20.13.41 - استعادة شريط إجراءات الفيديو غير المطوي</string>
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - استعادة أيقونات التنقل القديمة</string> <string name="revanced_spoof_app_version_target_entry_2">19.35.36 - استعادة أيقونات مشغل Shorts القديمة</string>
<string name="revanced_spoof_app_version_target_entry_3">19.01.34 - استعادة أيقونات التنقل القديمة</string>
</patch> </patch>
<patch id="layout.startpage.changeStartPagePatch"> <patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">تغيير صفحة البداية</string> <string name="revanced_change_start_page_title">تغيير صفحة البداية</string>
@@ -1585,11 +1590,46 @@ Second \"item\" text"</string>
<string name="revanced_music_play_permanent_repeat_summary_on">تم تمكين التكرار الدائم</string> <string name="revanced_music_play_permanent_repeat_summary_on">تم تمكين التكرار الدائم</string>
<string name="revanced_music_play_permanent_repeat_summary_off">تم تعطيل التكرار الدائم</string> <string name="revanced_music_play_permanent_repeat_summary_off">تم تعطيل التكرار الدائم</string>
</patch> </patch>
<patch id="layout.castbutton.hideCastButton">
<string name="revanced_music_hide_cast_button_title">إخفاء زر الإرسال</string>
<string name="revanced_music_hide_cast_button_summary_on">زر الإرسال مخفي</string>
<string name="revanced_music_hide_cast_button_summary_off">زر الإرسال ظاهر</string>
</patch>
<patch id="layout.compactheader.hideCategoryBar"> <patch id="layout.compactheader.hideCategoryBar">
<string name="revanced_music_hide_category_bar_title">إخفاء شريط الفئات</string> <string name="revanced_music_hide_category_bar_title">إخفاء شريط الفئات</string>
<string name="revanced_music_hide_category_bar_summary_on">شريط الفئات مخفي</string> <string name="revanced_music_hide_category_bar_summary_on">شريط الفئات مخفي</string>
<string name="revanced_music_hide_category_bar_summary_off">شريط الفئات معروض</string> <string name="revanced_music_hide_category_bar_summary_off">شريط الفئات معروض</string>
</patch> </patch>
<patch id="layout.navigationbar.navigationBarPatch">
<string name="revanced_music_navigation_bar_screen_title">شريط التنقل</string>
<string name="revanced_music_navigation_bar_screen_summary">إخفاء أزرار شريط التنقل أو تغييرها</string>
<!-- 'Home' should be translated using the same localized wording YouTube Music displays for the tab. -->
<string name="revanced_music_hide_navigation_bar_home_button_title">إخفاء زر \"الرئيسية\"</string>
<string name="revanced_music_hide_navigation_bar_home_button_summary_on">زر \"الرئيسية\" مخفي</string>
<string name="revanced_music_hide_navigation_bar_home_button_summary_off">زر \"الرئيسية\" ظاهر</string>
<!-- 'Samples' should be translated using the same localized wording YouTube Music displays for the tab. -->
<string name="revanced_music_hide_navigation_bar_samples_button_title">إخفاء زر \"المقتطفات\"</string>
<string name="revanced_music_hide_navigation_bar_samples_button_summary_on">زر \"المقتطفات\" مخفي</string>
<string name="revanced_music_hide_navigation_bar_samples_button_summary_off">زر \"المقتطفات\" ظاهر</string>
<!-- 'Explore' should be translated using the same localized wording YouTube Music displays for the tab. -->
<string name="revanced_music_hide_navigation_bar_explore_button_title">إخفاء زر \"استكشاف\"</string>
<string name="revanced_music_hide_navigation_bar_explore_button_summary_on">زر \"استكشاف\" مخفي</string>
<string name="revanced_music_hide_navigation_bar_explore_button_summary_off">زر \"استكشاف\" ظاهر</string>
<!-- 'Library' should be translated using the same localized wording YouTube Music displays for the tab. -->
<string name="revanced_music_hide_navigation_bar_library_button_title">إخفاء زر \"المكتبة\"</string>
<string name="revanced_music_hide_navigation_bar_library_button_summary_on">زر \"المكتبة\" مخفي</string>
<string name="revanced_music_hide_navigation_bar_library_button_summary_off">زر \"المكتبة\" ظاهر</string>
<!-- 'Upgrade' should be translated using the same localized wording YouTube Music displays for the tab. -->
<string name="revanced_music_hide_navigation_bar_upgrade_button_title">إخفاء زر \"الترقية\"</string>
<string name="revanced_music_hide_navigation_bar_upgrade_button_summary_on">زر \"الترقية\" مخفي</string>
<string name="revanced_music_hide_navigation_bar_upgrade_button_summary_off">زر \"الترقية\" ظاهر</string>
<string name="revanced_music_hide_navigation_bar_title">إخفاء شريط التنقل</string>
<string name="revanced_music_hide_navigation_bar_summary_on">شريط التنقل مخفي</string>
<string name="revanced_music_hide_navigation_bar_summary_off">شريط التنقل ظاهر</string>
<string name="revanced_music_hide_navigation_bar_labels_title">إخفاء تسميات أزرار التنقل</string>
<string name="revanced_music_hide_navigation_bar_labels_summary_on">التسميات مخفية</string>
<string name="revanced_music_hide_navigation_bar_labels_summary_off">التسميات ظاهرة</string>
</patch>
<patch id="layout.premium.hideGetPremiumPatch"> <patch id="layout.premium.hideGetPremiumPatch">
<string name="revanced_music_hide_get_premium_label_title">إخفاء تسمية \'الحصول على Music Premium\'</string> <string name="revanced_music_hide_get_premium_label_title">إخفاء تسمية \'الحصول على Music Premium\'</string>
<string name="revanced_music_hide_get_premium_label_summary_on">التسمية مخفية</string> <string name="revanced_music_hide_get_premium_label_summary_on">التسمية مخفية</string>

View File

@@ -89,6 +89,7 @@ Second \"item\" text"</string>
<patch id="layout.buttons.action.hideButtonsPatch"> <patch id="layout.buttons.action.hideButtonsPatch">
<!-- 'Share' should be translated with the same localized wording that YouTube displays. --> <!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Stop ads' should be translated with the same localized wording that YouTube displays. --> <!-- 'Stop ads' should be translated with the same localized wording that YouTube displays. -->
<!-- Button does not have any text and is only shown as an icon, and only when the video information area is collapsed to a compact state. -->
<!-- 'Report' should be translated with the same localized wording that YouTube displays. <!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually only shows on live streams. --> This button usually only shows on live streams. -->
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. --> <!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
@@ -255,8 +256,17 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="interaction.permanentrepeat.permanentRepeatPatch"> <patch id="interaction.permanentrepeat.permanentRepeatPatch">
</patch> </patch>
<patch id="layout.castbutton.hideCastButton">
</patch>
<patch id="layout.compactheader.hideCategoryBar"> <patch id="layout.compactheader.hideCategoryBar">
</patch> </patch>
<patch id="layout.navigationbar.navigationBarPatch">
<!-- 'Home' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Samples' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Explore' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Library' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Upgrade' should be translated using the same localized wording YouTube Music displays for the tab. -->
</patch>
<patch id="layout.premium.hideGetPremiumPatch"> <patch id="layout.premium.hideGetPremiumPatch">
</patch> </patch>
<patch id="layout.upgradebutton.hideUpgradeButtonPatch"> <patch id="layout.upgradebutton.hideUpgradeButtonPatch">

View File

@@ -107,6 +107,15 @@ Oynatma işləməyə bilər"</string>
<string name="revanced_debug_title">Sazlama jurnalı</string> <string name="revanced_debug_title">Sazlama jurnalı</string>
<string name="revanced_debug_summary_on">Sazlama jurnalı işləkdir</string> <string name="revanced_debug_summary_on">Sazlama jurnalı işləkdir</string>
<string name="revanced_debug_summary_off">Sazlama jurnalı qeyri-aktivdir</string> <string name="revanced_debug_summary_off">Sazlama jurnalı qeyri-aktivdir</string>
<string name="revanced_debug_stacktrace_title">Yığın izləri jurnalı</string>
<string name="revanced_debug_stacktrace_summary_on">Sazlama jurnalına yığın izləri daxildir</string>
<string name="revanced_debug_stacktrace_summary_off">Sazlama jurnalına yığın izləri daxil deyil</string>
<string name="revanced_debug_toast_on_error_title">ReVanced xətasında ani bildiriş göstər</string>
<string name="revanced_debug_toast_on_error_summary_on">Xəta baş verərsə ani bildiriş görünür</string>
<string name="revanced_debug_toast_on_error_summary_off">Xəta baş verərsə ani bildiriş görünmür</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">"Xəta ani bildirişlərin qapatmaq, bütün ReVanced xəta bildirişlərin gizlədir.
Gözlənilməz hallardan xəbərdar olmayacaqsınız."</string>
<string name="revanced_debug_export_logs_to_clipboard_title">Sazlama qeydlərini ixrac edin</string> <string name="revanced_debug_export_logs_to_clipboard_title">Sazlama qeydlərini ixrac edin</string>
<string name="revanced_debug_export_logs_to_clipboard_summary">ReVanced sazlama qeydlərini buferə köçürür</string> <string name="revanced_debug_export_logs_to_clipboard_summary">ReVanced sazlama qeydlərini buferə köçürür</string>
<string name="revanced_debug_logs_disabled">Sazlama qeydi qapalıdır</string> <string name="revanced_debug_logs_disabled">Sazlama qeydi qapalıdır</string>
@@ -151,15 +160,6 @@ Oynatma işləməyə bilər"</string>
Bu, xüsusi filtrlər yaradarkən quruluşları müəyyən etməyə kömək edə bilər. Bu, xüsusi filtrlər yaradarkən quruluşları müəyyən etməyə kömək edə bilər.
Hər halda, bunu aktivləşdirmə IP ünvanınız kimi bəzi istifadəçi məlumatın da daxil edəcək."</string> Hər halda, bunu aktivləşdirmə IP ünvanınız kimi bəzi istifadəçi məlumatın da daxil edəcək."</string>
<string name="revanced_debug_stacktrace_title">Yığın izləri jurnalı</string>
<string name="revanced_debug_stacktrace_summary_on">Sazlama jurnalına yığın izləri daxildir</string>
<string name="revanced_debug_stacktrace_summary_off">Sazlama jurnalına yığın izləri daxil deyil</string>
<string name="revanced_debug_toast_on_error_title">ReVanced xətasında ani bildiriş göstər</string>
<string name="revanced_debug_toast_on_error_summary_on">Xəta baş verərsə ani bildiriş görünür</string>
<string name="revanced_debug_toast_on_error_summary_off">Xəta baş verərsə ani bildiriş görünmür</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">"Xəta ani bildirişlərin qapatmaq, bütün ReVanced xəta bildirişlərin gizlədir.
Gözlənilməz hallardan xəbərdar olmayacaqsınız."</string>
</patch> </patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch"> <patch id="layout.hide.general.hideLayoutComponentsPatch">
<string name="revanced_hide_album_cards_title">Albom kartlarını gizlət</string> <string name="revanced_hide_album_cards_title">Albom kartlarını gizlət</string>
@@ -586,6 +586,7 @@ Ekranın sağ tərəfində düzünə sürüşdürərək səs səviyyəsini tənz
<string name="revanced_hide_stop_ads_button_title">Reklamları Dayandırın-ı gizlət</string> <string name="revanced_hide_stop_ads_button_title">Reklamları Dayandırın-ı gizlət</string>
<string name="revanced_hide_stop_ads_button_summary_on">Reklamları dayandır düyməsi gizlidir</string> <string name="revanced_hide_stop_ads_button_summary_on">Reklamları dayandır düyməsi gizlidir</string>
<string name="revanced_hide_stop_ads_button_summary_off">Reklamları dayandır düyməsi görünür</string> <string name="revanced_hide_stop_ads_button_summary_off">Reklamları dayandır düyməsi görünür</string>
<!-- Button does not have any text and is only shown as an icon, and only when the video information area is collapsed to a compact state. -->
<!-- 'Report' should be translated with the same localized wording that YouTube displays. <!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually only shows on live streams. --> This button usually only shows on live streams. -->
<string name="revanced_hide_report_button_title">\"Xəbər verin\"i gizlət</string> <string name="revanced_hide_report_button_title">\"Xəbər verin\"i gizlət</string>
@@ -1241,8 +1242,8 @@ Bu tətbiqin görünüşün və xüsusiyyətlərin dəyişdirəcək, lakin bilin
Sonradan qapadılarsa, UI səhvlərin önləmək üçün tətbiq məlumatların silmək tövsiyə olunur."</string> Sonradan qapadılarsa, UI səhvlərin önləmək üçün tətbiq məlumatların silmək tövsiyə olunur."</string>
<string name="revanced_spoof_app_version_target_title">Saxta tətbiq versiyası hədəfi</string> <string name="revanced_spoof_app_version_target_title">Saxta tətbiq versiyası hədəfi</string>
<string name="revanced_spoof_app_version_target_entry_1">19.35.36 - Köhnə Shorts oynadıcı işarələrin bərpa et</string> <string name="revanced_spoof_app_version_target_entry_2">19.35.36 - Köhnə Shorts oynadıcı işarələrin bərpa et</string>
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - Köhnə fəaliyyət simvolların bərpa et</string> <string name="revanced_spoof_app_version_target_entry_3">19.01.34 - Köhnə fəaliyyət simvolların bərpa et</string>
</patch> </patch>
<patch id="layout.startpage.changeStartPagePatch"> <patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">Başlatma səhifəsini dəyişdir</string> <string name="revanced_change_start_page_title">Başlatma səhifəsini dəyişdir</string>
@@ -1584,11 +1585,20 @@ Bunu aktivləşdirmə daha yüksək video keyfiyyətləri əngəlin silə bilər
<string name="revanced_music_play_permanent_repeat_summary_on">Kəsintisiz təkrarlama aktivdir</string> <string name="revanced_music_play_permanent_repeat_summary_on">Kəsintisiz təkrarlama aktivdir</string>
<string name="revanced_music_play_permanent_repeat_summary_off">Kəsintisiz təkrarlama qapalıdır</string> <string name="revanced_music_play_permanent_repeat_summary_off">Kəsintisiz təkrarlama qapalıdır</string>
</patch> </patch>
<patch id="layout.castbutton.hideCastButton">
</patch>
<patch id="layout.compactheader.hideCategoryBar"> <patch id="layout.compactheader.hideCategoryBar">
<string name="revanced_music_hide_category_bar_title">Kateqoriya cizgisin gizlət</string> <string name="revanced_music_hide_category_bar_title">Kateqoriya cizgisin gizlət</string>
<string name="revanced_music_hide_category_bar_summary_on">Kateqoriya cizgisi gizlidir</string> <string name="revanced_music_hide_category_bar_summary_on">Kateqoriya cizgisi gizlidir</string>
<string name="revanced_music_hide_category_bar_summary_off">Kateqoriya cizgisi görünür</string> <string name="revanced_music_hide_category_bar_summary_off">Kateqoriya cizgisi görünür</string>
</patch> </patch>
<patch id="layout.navigationbar.navigationBarPatch">
<!-- 'Home' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Samples' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Explore' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Library' should be translated using the same localized wording YouTube Music displays for the tab. -->
<!-- 'Upgrade' should be translated using the same localized wording YouTube Music displays for the tab. -->
</patch>
<patch id="layout.premium.hideGetPremiumPatch"> <patch id="layout.premium.hideGetPremiumPatch">
<string name="revanced_music_hide_get_premium_label_title"> \'Musiqi Premiumu Əldə et\' etiketini gizlət</string> <string name="revanced_music_hide_get_premium_label_title"> \'Musiqi Premiumu Əldə et\' etiketini gizlət</string>
<string name="revanced_music_hide_get_premium_label_summary_on">Etiket gizlidir</string> <string name="revanced_music_hide_get_premium_label_summary_on">Etiket gizlidir</string>

Some files were not shown because too many files have changed in this diff Show More