mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-06-13 05:37:40 +02:00
fix(YouTube Music - Spoof client): Action bar not loading as of YouTube Music 7.17.51
This commit is contained in:
@ -1,25 +1,32 @@
|
|||||||
package app.revanced.extension.music.patches.misc;
|
package app.revanced.extension.music.patches.misc;
|
||||||
|
|
||||||
import app.revanced.extension.music.settings.Settings;
|
import app.revanced.extension.shared.patches.BlockRequestPatch;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class SpoofClientPatch {
|
public class SpoofClientPatch extends BlockRequestPatch {
|
||||||
private static final int CLIENT_ID_IOS_MUSIC = 26;
|
private static final int CLIENT_ID_IOS_MUSIC = 26;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The hardcoded client version of the iOS app used for InnerTube requests with this client.
|
* 1. {@link BlockRequestPatch} is not required, as the litho component is not applied to the video action bar and flyout menu.
|
||||||
*
|
* 2. Audio codec is MP4A.
|
||||||
|
*/
|
||||||
|
private static final String CLIENT_VERSION_IOS_MUSIC_6_21 = "6.21";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. {@link BlockRequestPatch} is required, as the layout used in iOS should be prevented from being applied to the video action bar and flyout menu.
|
||||||
|
* 2. Audio codec is OPUS.
|
||||||
|
*/
|
||||||
|
private static final String CLIENT_VERSION_IOS_MUSIC_7_31 = "7.31.2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starting with YouTube Music 7.17.51+, the litho component has been applied to the video action bar.
|
||||||
* <p>
|
* <p>
|
||||||
* It can be extracted by getting the latest release version of the app on
|
* So if {@code CLIENT_VERSION_IOS_MUSIC_6_21} is used in YouTube Music 7.17.51+,
|
||||||
* <a href="https://apps.apple.com/us/app/youtube-music/id1017492454/">the App
|
* the video action bar will not load properly.
|
||||||
* Store page of the YouTube app</a>, in the {@code What<61><74>s New} section.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
private static final String CLIENT_VERSION_IOS_MUSIC = "6.21";
|
|
||||||
/**
|
|
||||||
* See <a href="https://gist.github.com/adamawolf/3048717">this GitHub Gist</a> for more
|
|
||||||
* information.
|
|
||||||
* </p>
|
|
||||||
*/
|
*/
|
||||||
|
private static final String CLIENT_VERSION_IOS_MUSIC = IS_7_17_OR_GREATER
|
||||||
|
? CLIENT_VERSION_IOS_MUSIC_6_21
|
||||||
|
: CLIENT_VERSION_IOS_MUSIC_7_31;
|
||||||
private static final String DEVICE_MODEL_IOS_MUSIC = "iPhone16,2";
|
private static final String DEVICE_MODEL_IOS_MUSIC = "iPhone16,2";
|
||||||
private static final String OS_VERSION_IOS_MUSIC = "17.7.2.21H221";
|
private static final String OS_VERSION_IOS_MUSIC = "17.7.2.21H221";
|
||||||
private static final String USER_AGENT_VERSION_IOS_MUSIC = "17_7_2";
|
private static final String USER_AGENT_VERSION_IOS_MUSIC = "17_7_2";
|
||||||
@ -31,13 +38,11 @@ public class SpoofClientPatch {
|
|||||||
USER_AGENT_VERSION_IOS_MUSIC +
|
USER_AGENT_VERSION_IOS_MUSIC +
|
||||||
" like Mac OS X)";
|
" like Mac OS X)";
|
||||||
|
|
||||||
private static final boolean SPOOF_CLIENT_ENABLED = Settings.SPOOF_CLIENT.get();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static int getClientTypeId(int originalClientTypeId) {
|
public static int getClientTypeId(int originalClientTypeId) {
|
||||||
if (SPOOF_CLIENT_ENABLED) {
|
if (SPOOF_CLIENT) {
|
||||||
return CLIENT_ID_IOS_MUSIC;
|
return CLIENT_ID_IOS_MUSIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +53,7 @@ public class SpoofClientPatch {
|
|||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static String getClientVersion(String originalClientVersion) {
|
public static String getClientVersion(String originalClientVersion) {
|
||||||
if (SPOOF_CLIENT_ENABLED) {
|
if (SPOOF_CLIENT) {
|
||||||
return CLIENT_VERSION_IOS_MUSIC;
|
return CLIENT_VERSION_IOS_MUSIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +64,7 @@ public class SpoofClientPatch {
|
|||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static String getClientModel(String originalClientModel) {
|
public static String getClientModel(String originalClientModel) {
|
||||||
if (SPOOF_CLIENT_ENABLED) {
|
if (SPOOF_CLIENT) {
|
||||||
return DEVICE_MODEL_IOS_MUSIC;
|
return DEVICE_MODEL_IOS_MUSIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +75,7 @@ public class SpoofClientPatch {
|
|||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static String getOsVersion(String originalOsVersion) {
|
public static String getOsVersion(String originalOsVersion) {
|
||||||
if (SPOOF_CLIENT_ENABLED) {
|
if (SPOOF_CLIENT) {
|
||||||
return OS_VERSION_IOS_MUSIC;
|
return OS_VERSION_IOS_MUSIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +86,7 @@ public class SpoofClientPatch {
|
|||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static String getUserAgent(String originalUserAgent) {
|
public static String getUserAgent(String originalUserAgent) {
|
||||||
if (SPOOF_CLIENT_ENABLED) {
|
if (SPOOF_CLIENT) {
|
||||||
return USER_AGENT_IOS_MUSIC;
|
return USER_AGENT_IOS_MUSIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,16 +97,19 @@ public class SpoofClientPatch {
|
|||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static boolean isClientSpoofingEnabled() {
|
public static boolean isClientSpoofingEnabled() {
|
||||||
return SPOOF_CLIENT_ENABLED;
|
return SPOOF_CLIENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
|
* <p>
|
||||||
* When spoofing the client to iOS, the playback speed menu is missing from the player response.
|
* When spoofing the client to iOS, the playback speed menu is missing from the player response.
|
||||||
|
* This fix is required because playback speed is not available in YouTube Music Podcasts.
|
||||||
|
* <p>
|
||||||
* Return true to force create the playback speed menu.
|
* Return true to force create the playback speed menu.
|
||||||
*/
|
*/
|
||||||
public static boolean forceCreatePlaybackSpeedMenu(boolean original) {
|
public static boolean forceCreatePlaybackSpeedMenu(boolean original) {
|
||||||
if (SPOOF_CLIENT_ENABLED) {
|
if (SPOOF_CLIENT) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return original;
|
return original;
|
||||||
|
@ -175,7 +175,6 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting DISABLE_CAIRO_SPLASH_ANIMATION = new BooleanSetting("revanced_disable_cairo_splash_animation", FALSE, true);
|
public static final BooleanSetting DISABLE_CAIRO_SPLASH_ANIMATION = new BooleanSetting("revanced_disable_cairo_splash_animation", FALSE, true);
|
||||||
public static final BooleanSetting DISABLE_DRC_AUDIO = new BooleanSetting("revanced_disable_drc_audio", FALSE, true);
|
public static final BooleanSetting DISABLE_DRC_AUDIO = new BooleanSetting("revanced_disable_drc_audio", FALSE, true);
|
||||||
public static final BooleanSetting ENABLE_OPUS_CODEC = new BooleanSetting("revanced_enable_opus_codec", FALSE, true);
|
public static final BooleanSetting ENABLE_OPUS_CODEC = new BooleanSetting("revanced_enable_opus_codec", FALSE, true);
|
||||||
public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", FALSE, true);
|
|
||||||
public static final BooleanSetting SETTINGS_IMPORT_EXPORT = new BooleanSetting("revanced_extended_settings_import_export", FALSE, false);
|
public static final BooleanSetting SETTINGS_IMPORT_EXPORT = new BooleanSetting("revanced_extended_settings_import_export", FALSE, false);
|
||||||
|
|
||||||
|
|
||||||
@ -239,6 +238,7 @@ public class Settings extends BaseSettings {
|
|||||||
SB_API_URL.key,
|
SB_API_URL.key,
|
||||||
SETTINGS_IMPORT_EXPORT.key,
|
SETTINGS_IMPORT_EXPORT.key,
|
||||||
SPOOF_APP_VERSION_TARGET.key,
|
SPOOF_APP_VERSION_TARGET.key,
|
||||||
|
SPOOF_STREAMING_DATA_TYPE.key,
|
||||||
RETURN_YOUTUBE_USERNAME_ABOUT.key,
|
RETURN_YOUTUBE_USERNAME_ABOUT.key,
|
||||||
RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT.key,
|
RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT.key,
|
||||||
RETURN_YOUTUBE_USERNAME_YOUTUBE_DATA_API_V3_DEVELOPER_KEY.key,
|
RETURN_YOUTUBE_USERNAME_YOUTUBE_DATA_API_V3_DEVELOPER_KEY.key,
|
||||||
|
@ -17,6 +17,7 @@ import static app.revanced.extension.music.utils.ExtendedUtils.getLayoutParams;
|
|||||||
import static app.revanced.extension.music.utils.RestartUtils.showRestartDialog;
|
import static app.revanced.extension.music.utils.RestartUtils.showRestartDialog;
|
||||||
import static app.revanced.extension.shared.settings.BaseSettings.RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT;
|
import static app.revanced.extension.shared.settings.BaseSettings.RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT;
|
||||||
import static app.revanced.extension.shared.settings.BaseSettings.RETURN_YOUTUBE_USERNAME_YOUTUBE_DATA_API_V3_DEVELOPER_KEY;
|
import static app.revanced.extension.shared.settings.BaseSettings.RETURN_YOUTUBE_USERNAME_YOUTUBE_DATA_API_V3_DEVELOPER_KEY;
|
||||||
|
import static app.revanced.extension.shared.settings.BaseSettings.SPOOF_STREAMING_DATA_TYPE;
|
||||||
import static app.revanced.extension.shared.settings.Setting.getSettingFromPath;
|
import static app.revanced.extension.shared.settings.Setting.getSettingFromPath;
|
||||||
import static app.revanced.extension.shared.utils.ResourceUtils.getStringArray;
|
import static app.revanced.extension.shared.utils.ResourceUtils.getStringArray;
|
||||||
import static app.revanced.extension.shared.utils.StringRef.str;
|
import static app.revanced.extension.shared.utils.StringRef.str;
|
||||||
@ -158,7 +159,8 @@ public class ReVancedPreferenceFragment extends PreferenceFragment {
|
|||||||
Logger.printDebug(() -> "Failed to find the right value: " + dataString);
|
Logger.printDebug(() -> "Failed to find the right value: " + dataString);
|
||||||
}
|
}
|
||||||
} else if (settings instanceof EnumSetting<?> enumSetting) {
|
} else if (settings instanceof EnumSetting<?> enumSetting) {
|
||||||
if (settings.equals(RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT)) {
|
if (settings.equals(RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT)
|
||||||
|
|| settings.equals(SPOOF_STREAMING_DATA_TYPE)) {
|
||||||
ResettableListPreference.showDialog(mActivity, enumSetting, 0);
|
ResettableListPreference.showDialog(mActivity, enumSetting, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
package app.revanced.extension.shared.patches;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import app.revanced.extension.music.settings.Settings;
|
||||||
|
import app.revanced.extension.shared.utils.Logger;
|
||||||
|
import app.revanced.extension.shared.utils.PackageUtils;
|
||||||
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class BlockRequestPatch {
|
||||||
|
/**
|
||||||
|
* Used in YouTube and YouTube Music.
|
||||||
|
*/
|
||||||
|
public static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_STREAMING_DATA.get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used in YouTube Music.
|
||||||
|
* Disabled by default.
|
||||||
|
*/
|
||||||
|
public static final boolean SPOOF_CLIENT = Settings.SPOOF_CLIENT.get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used in YouTube Music.
|
||||||
|
*/
|
||||||
|
public static final boolean IS_7_17_OR_GREATER = PackageUtils.getAppVersionName().compareTo("7.17.00") >= 0;
|
||||||
|
|
||||||
|
private static final boolean BLOCK_REQUEST = SPOOF_STREAMING_DATA || (SPOOF_CLIENT && IS_7_17_OR_GREATER);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any unreachable ip address. Used to intentionally fail requests.
|
||||||
|
*/
|
||||||
|
private static final String UNREACHABLE_HOST_URI_STRING = "https://127.0.0.0";
|
||||||
|
private static final Uri UNREACHABLE_HOST_URI = Uri.parse(UNREACHABLE_HOST_URI_STRING);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
* Blocks /get_watch requests by returning an unreachable URI.
|
||||||
|
*
|
||||||
|
* @param playerRequestUri The URI of the player request.
|
||||||
|
* @return An unreachable URI if the request is a /get_watch request, otherwise the original URI.
|
||||||
|
*/
|
||||||
|
public static Uri blockGetWatchRequest(Uri playerRequestUri) {
|
||||||
|
if (BLOCK_REQUEST) {
|
||||||
|
try {
|
||||||
|
String path = playerRequestUri.getPath();
|
||||||
|
|
||||||
|
if (path != null && path.contains("get_watch")) {
|
||||||
|
Logger.printDebug(() -> "Blocking 'get_watch' by returning unreachable uri");
|
||||||
|
|
||||||
|
return UNREACHABLE_HOST_URI;
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "blockGetWatchRequest failure", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return playerRequestUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
* <p>
|
||||||
|
* Blocks /initplayback requests.
|
||||||
|
*/
|
||||||
|
public static String blockInitPlaybackRequest(String originalUrlString) {
|
||||||
|
if (BLOCK_REQUEST) {
|
||||||
|
try {
|
||||||
|
var originalUri = Uri.parse(originalUrlString);
|
||||||
|
String path = originalUri.getPath();
|
||||||
|
|
||||||
|
if (path != null && path.contains("initplayback")) {
|
||||||
|
Logger.printDebug(() -> "Blocking 'initplayback' by clearing query");
|
||||||
|
|
||||||
|
return originalUri.buildUpon().clearQuery().build().toString();
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "blockInitPlaybackRequest failure", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return originalUrlString;
|
||||||
|
}
|
||||||
|
}
|
@ -41,19 +41,19 @@ public class AppClient {
|
|||||||
USER_AGENT_VERSION_IOS +
|
USER_AGENT_VERSION_IOS +
|
||||||
" like Mac OS X)";
|
" like Mac OS X)";
|
||||||
|
|
||||||
// IOS_MUSIC
|
// IOS MUSIC
|
||||||
/**
|
/**
|
||||||
* The hardcoded client version of the iOS app used for InnerTube requests with this client.
|
* The hardcoded client version of the iOS app used for InnerTube requests with this client.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* It can be extracted by getting the latest release version of the app on
|
* It can be extracted by getting the latest release version of the app on
|
||||||
* <a href="https://apps.apple.com/us/app/youtube-music/id1017492454/">the App
|
* <a href="https://apps.apple.com/us/app/youtube-music/id1017492454/">the App
|
||||||
* Store page of the YouTube app</a>, in the {@code What’s New} section.
|
* Store page of the YouTube Music app</a>, in the {@code What’s New} section.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
private static final String CLIENT_VERSION_IOS_MUSIC = "7.31.2";
|
private static final String CLIENT_VERSION_IOS_MUSIC = "7.31.2";
|
||||||
private static final String USER_AGENT_IOS_MUSIC = "com.google.ios.youtubemusic/" +
|
private static final String USER_AGENT_IOS_MUSIC = "com.google.ios.youtubemusic/" +
|
||||||
CLIENT_VERSION_IOS +
|
CLIENT_VERSION_IOS_MUSIC +
|
||||||
"(" +
|
"(" +
|
||||||
DEVICE_MODEL_IOS +
|
DEVICE_MODEL_IOS +
|
||||||
"; U; CPU iOS " +
|
"; U; CPU iOS " +
|
||||||
|
@ -8,68 +8,14 @@ import androidx.annotation.Nullable;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.patches.BlockRequestPatch;
|
||||||
import app.revanced.extension.shared.utils.Logger;
|
import app.revanced.extension.shared.utils.Logger;
|
||||||
import app.revanced.extension.shared.utils.Utils;
|
import app.revanced.extension.shared.utils.Utils;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.shared.patches.spoof.requests.StreamingDataRequest;
|
import app.revanced.extension.shared.patches.spoof.requests.StreamingDataRequest;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class SpoofStreamingDataPatch {
|
public class SpoofStreamingDataPatch extends BlockRequestPatch {
|
||||||
private static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_STREAMING_DATA.get();
|
|
||||||
/**
|
|
||||||
* Any unreachable ip address. Used to intentionally fail requests.
|
|
||||||
*/
|
|
||||||
private static final String UNREACHABLE_HOST_URI_STRING = "https://127.0.0.0";
|
|
||||||
private static final Uri UNREACHABLE_HOST_URI = Uri.parse(UNREACHABLE_HOST_URI_STRING);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point.
|
|
||||||
* Blocks /get_watch requests by returning an unreachable URI.
|
|
||||||
*
|
|
||||||
* @param playerRequestUri The URI of the player request.
|
|
||||||
* @return An unreachable URI if the request is a /get_watch request, otherwise the original URI.
|
|
||||||
*/
|
|
||||||
public static Uri blockGetWatchRequest(Uri playerRequestUri) {
|
|
||||||
if (SPOOF_STREAMING_DATA) {
|
|
||||||
try {
|
|
||||||
String path = playerRequestUri.getPath();
|
|
||||||
|
|
||||||
if (path != null && path.contains("get_watch")) {
|
|
||||||
Logger.printDebug(() -> "Blocking 'get_watch' by returning unreachable uri");
|
|
||||||
|
|
||||||
return UNREACHABLE_HOST_URI;
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "blockGetWatchRequest failure", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return playerRequestUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point.
|
|
||||||
* <p>
|
|
||||||
* Blocks /initplayback requests.
|
|
||||||
*/
|
|
||||||
public static String blockInitPlaybackRequest(String originalUrlString) {
|
|
||||||
if (SPOOF_STREAMING_DATA) {
|
|
||||||
try {
|
|
||||||
var originalUri = Uri.parse(originalUrlString);
|
|
||||||
String path = originalUri.getPath();
|
|
||||||
|
|
||||||
if (path != null && path.contains("initplayback")) {
|
|
||||||
Logger.printDebug(() -> "Blocking 'initplayback' by clearing query");
|
|
||||||
|
|
||||||
return originalUri.buildUpon().clearQuery().build().toString();
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "blockInitPlaybackRequest failure", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return originalUrlString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
|
@ -37,6 +37,10 @@ public class BaseSettings {
|
|||||||
public static final EnumSetting<DisplayFormat> RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT = new EnumSetting<>("revanced_return_youtube_username_display_format", DisplayFormat.USERNAME_ONLY, true);
|
public static final EnumSetting<DisplayFormat> RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT = new EnumSetting<>("revanced_return_youtube_username_display_format", DisplayFormat.USERNAME_ONLY, true);
|
||||||
public static final StringSetting RETURN_YOUTUBE_USERNAME_YOUTUBE_DATA_API_V3_DEVELOPER_KEY = new StringSetting("revanced_return_youtube_username_youtube_data_api_v3_developer_key", "", true, false);
|
public static final StringSetting RETURN_YOUTUBE_USERNAME_YOUTUBE_DATA_API_V3_DEVELOPER_KEY = new StringSetting("revanced_return_youtube_username_youtube_data_api_v3_developer_key", "", true, false);
|
||||||
|
|
||||||
|
// Used only in YouTube Music.
|
||||||
|
// Moved to a shared class to prevent YouTube from accessing YouTube Music's extension classes.
|
||||||
|
public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", FALSE, true);
|
||||||
|
|
||||||
public static final BooleanSetting SPOOF_STREAMING_DATA = new BooleanSetting("revanced_spoof_streaming_data", TRUE, true, "revanced_spoof_streaming_data_user_dialog_message");
|
public static final BooleanSetting SPOOF_STREAMING_DATA = new BooleanSetting("revanced_spoof_streaming_data", TRUE, true, "revanced_spoof_streaming_data_user_dialog_message");
|
||||||
public static final EnumSetting<ClientType> SPOOF_STREAMING_DATA_TYPE = new EnumSetting<>("revanced_spoof_streaming_data_type", SpoofStreamingDataDefaultClient(), true);
|
public static final EnumSetting<ClientType> SPOOF_STREAMING_DATA_TYPE = new EnumSetting<>("revanced_spoof_streaming_data_type", SpoofStreamingDataDefaultClient(), true);
|
||||||
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE);
|
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE);
|
||||||
|
@ -15,6 +15,7 @@ import app.revanced.patches.music.utils.settings.CategoryType
|
|||||||
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
|
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
|
||||||
import app.revanced.patches.music.utils.settings.addSwitchPreference
|
import app.revanced.patches.music.utils.settings.addSwitchPreference
|
||||||
import app.revanced.patches.music.utils.settings.settingsPatch
|
import app.revanced.patches.music.utils.settings.settingsPatch
|
||||||
|
import app.revanced.patches.shared.blockrequest.blockRequestPatch
|
||||||
import app.revanced.patches.shared.createPlayerRequestBodyWithModelFingerprint
|
import app.revanced.patches.shared.createPlayerRequestBodyWithModelFingerprint
|
||||||
import app.revanced.patches.shared.indexOfModelInstruction
|
import app.revanced.patches.shared.indexOfModelInstruction
|
||||||
import app.revanced.util.fingerprint.matchOrThrow
|
import app.revanced.util.fingerprint.matchOrThrow
|
||||||
@ -48,7 +49,10 @@ val spoofClientPatch = bytecodePatch(
|
|||||||
SPOOF_CLIENT.summary,
|
SPOOF_CLIENT.summary,
|
||||||
false,
|
false,
|
||||||
) {
|
) {
|
||||||
dependsOn(settingsPatch)
|
dependsOn(
|
||||||
|
settingsPatch,
|
||||||
|
blockRequestPatch
|
||||||
|
)
|
||||||
|
|
||||||
compatibleWith(COMPATIBLE_PACKAGE)
|
compatibleWith(COMPATIBLE_PACKAGE)
|
||||||
|
|
||||||
@ -211,6 +215,8 @@ val spoofClientPatch = bytecodePatch(
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
// region fix for playback speed menu is not available in Podcasts
|
||||||
|
|
||||||
playbackSpeedBottomSheetFingerprint.mutableClassOrThrow().let {
|
playbackSpeedBottomSheetFingerprint.mutableClassOrThrow().let {
|
||||||
val onItemClickMethod =
|
val onItemClickMethod =
|
||||||
it.methods.find { method -> method.name == "onItemClick" }
|
it.methods.find { method -> method.name == "onItemClick" }
|
||||||
@ -244,6 +250,8 @@ val spoofClientPatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
addSwitchPreference(
|
addSwitchPreference(
|
||||||
CategoryType.MISC,
|
CategoryType.MISC,
|
||||||
"revanced_spoof_client",
|
"revanced_spoof_client",
|
||||||
|
@ -7,6 +7,7 @@ import app.revanced.patches.music.utils.compatibility.Constants.YOUTUBE_MUSIC_PA
|
|||||||
import app.revanced.patches.music.utils.patch.PatchList.SPOOF_STREAMING_DATA
|
import app.revanced.patches.music.utils.patch.PatchList.SPOOF_STREAMING_DATA
|
||||||
import app.revanced.patches.music.utils.settings.CategoryType
|
import app.revanced.patches.music.utils.settings.CategoryType
|
||||||
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
|
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
|
||||||
|
import app.revanced.patches.music.utils.settings.addPreferenceWithIntent
|
||||||
import app.revanced.patches.music.utils.settings.addSwitchPreference
|
import app.revanced.patches.music.utils.settings.addSwitchPreference
|
||||||
import app.revanced.patches.music.utils.settings.settingsPatch
|
import app.revanced.patches.music.utils.settings.settingsPatch
|
||||||
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
|
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
|
||||||
@ -19,6 +20,7 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
|||||||
|
|
||||||
private const val DEFAULT_CLIENT_TYPE = "ANDROID_VR"
|
private const val DEFAULT_CLIENT_TYPE = "ANDROID_VR"
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
|
val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
|
||||||
{
|
{
|
||||||
compatibleWith(COMPATIBLE_PACKAGE)
|
compatibleWith(COMPATIBLE_PACKAGE)
|
||||||
@ -45,6 +47,11 @@ val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
|
|||||||
"revanced_spoof_streaming_data",
|
"revanced_spoof_streaming_data",
|
||||||
"true"
|
"true"
|
||||||
)
|
)
|
||||||
|
addPreferenceWithIntent(
|
||||||
|
CategoryType.MISC,
|
||||||
|
"revanced_spoof_streaming_data_type",
|
||||||
|
"revanced_spoof_streaming_data"
|
||||||
|
)
|
||||||
addSwitchPreference(
|
addSwitchPreference(
|
||||||
CategoryType.MISC,
|
CategoryType.MISC,
|
||||||
"revanced_spoof_streaming_data_stats_for_nerds",
|
"revanced_spoof_streaming_data_stats_for_nerds",
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package app.revanced.patches.shared.blockrequest
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
|
||||||
|
import app.revanced.util.fingerprint.matchOrThrow
|
||||||
|
import app.revanced.util.fingerprint.methodOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
|
const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
|
"$PATCHES_PATH/BlockRequestPatch;"
|
||||||
|
|
||||||
|
val blockRequestPatch = bytecodePatch(
|
||||||
|
description = "blockRequestPatch"
|
||||||
|
) {
|
||||||
|
execute {
|
||||||
|
// region Block /initplayback requests to fall back to /get_watch requests.
|
||||||
|
|
||||||
|
buildInitPlaybackRequestFingerprint.matchOrThrow().let {
|
||||||
|
it.method.apply {
|
||||||
|
val moveUriStringIndex = it.patternMatch!!.startIndex
|
||||||
|
val targetRegister =
|
||||||
|
getInstruction<OneRegisterInstruction>(moveUriStringIndex).registerA
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
moveUriStringIndex + 1,
|
||||||
|
"""
|
||||||
|
invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String;
|
||||||
|
move-result-object v$targetRegister
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Block /get_watch requests to fall back to /player requests.
|
||||||
|
|
||||||
|
buildPlayerRequestURIFingerprint.methodOrThrow().apply {
|
||||||
|
val invokeToStringIndex = indexOfToStringInstruction(this)
|
||||||
|
val uriRegister =
|
||||||
|
getInstruction<FiveRegisterInstruction>(invokeToStringIndex).registerC
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
invokeToStringIndex,
|
||||||
|
"""
|
||||||
|
invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri;
|
||||||
|
move-result-object v$uriRegister
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package app.revanced.patches.shared.blockrequest
|
||||||
|
|
||||||
|
import app.revanced.util.fingerprint.legacyFingerprint
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstruction
|
||||||
|
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 buildInitPlaybackRequestFingerprint = legacyFingerprint(
|
||||||
|
name = "buildInitPlaybackRequestFingerprint",
|
||||||
|
returnType = "Lorg/chromium/net/UrlRequest\$Builder;",
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT, // Moves the request URI string to a register to build the request with.
|
||||||
|
),
|
||||||
|
strings = listOf(
|
||||||
|
"Content-Type",
|
||||||
|
"Range",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
internal val buildPlayerRequestURIFingerprint = legacyFingerprint(
|
||||||
|
name = "buildPlayerRequestURIFingerprint",
|
||||||
|
returnType = "Ljava/lang/String;",
|
||||||
|
strings = listOf(
|
||||||
|
"key",
|
||||||
|
"asig",
|
||||||
|
),
|
||||||
|
customFingerprint = { method, _ ->
|
||||||
|
indexOfToStringInstruction(method) >= 0
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun indexOfToStringInstruction(method: Method) =
|
||||||
|
method.indexOfFirstInstruction {
|
||||||
|
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||||
|
getReference<MethodReference>().toString() == "Landroid/net/Uri;->toString()Ljava/lang/String;"
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ import app.revanced.patcher.patch.BytecodePatchContext
|
|||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
|
import app.revanced.patches.shared.blockrequest.blockRequestPatch
|
||||||
import app.revanced.patches.shared.extension.Constants.SPOOF_PATH
|
import app.revanced.patches.shared.extension.Constants.SPOOF_PATH
|
||||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||||
import app.revanced.util.fingerprint.definingClassOrThrow
|
import app.revanced.util.fingerprint.definingClassOrThrow
|
||||||
@ -32,47 +33,11 @@ fun baseSpoofStreamingDataPatch(
|
|||||||
name = "Spoof streaming data",
|
name = "Spoof streaming data",
|
||||||
description = "Adds options to spoof the streaming data to allow playback."
|
description = "Adds options to spoof the streaming data to allow playback."
|
||||||
) {
|
) {
|
||||||
|
dependsOn(blockRequestPatch)
|
||||||
|
|
||||||
block()
|
block()
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
// region Block /initplayback requests to fall back to /get_watch requests.
|
|
||||||
|
|
||||||
buildInitPlaybackRequestFingerprint.matchOrThrow().let {
|
|
||||||
it.method.apply {
|
|
||||||
val moveUriStringIndex = it.patternMatch!!.startIndex
|
|
||||||
val targetRegister =
|
|
||||||
getInstruction<OneRegisterInstruction>(moveUriStringIndex).registerA
|
|
||||||
|
|
||||||
addInstructions(
|
|
||||||
moveUriStringIndex + 1,
|
|
||||||
"""
|
|
||||||
invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String;
|
|
||||||
move-result-object v$targetRegister
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Block /get_watch requests to fall back to /player requests.
|
|
||||||
|
|
||||||
buildPlayerRequestURIFingerprint.methodOrThrow().apply {
|
|
||||||
val invokeToStringIndex = indexOfToStringInstruction(this)
|
|
||||||
val uriRegister =
|
|
||||||
getInstruction<FiveRegisterInstruction>(invokeToStringIndex).registerC
|
|
||||||
|
|
||||||
addInstructions(
|
|
||||||
invokeToStringIndex,
|
|
||||||
"""
|
|
||||||
invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri;
|
|
||||||
move-result-object v$uriRegister
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Get replacement streams at player requests.
|
// region Get replacement streams at player requests.
|
||||||
|
|
||||||
buildRequestFingerprint.methodOrThrow().apply {
|
buildRequestFingerprint.methodOrThrow().apply {
|
||||||
|
@ -7,22 +7,8 @@ import app.revanced.util.or
|
|||||||
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.Method
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
internal val buildInitPlaybackRequestFingerprint = legacyFingerprint(
|
|
||||||
name = "buildInitPlaybackRequestFingerprint",
|
|
||||||
returnType = "Lorg/chromium/net/UrlRequest\$Builder;",
|
|
||||||
opcodes = listOf(
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT, // Moves the request URI string to a register to build the request with.
|
|
||||||
),
|
|
||||||
strings = listOf(
|
|
||||||
"Content-Type",
|
|
||||||
"Range",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
internal val buildMediaDataSourceFingerprint = legacyFingerprint(
|
internal val buildMediaDataSourceFingerprint = legacyFingerprint(
|
||||||
name = "buildMediaDataSourceFingerprint",
|
name = "buildMediaDataSourceFingerprint",
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||||
@ -41,24 +27,6 @@ internal val buildMediaDataSourceFingerprint = legacyFingerprint(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val buildPlayerRequestURIFingerprint = legacyFingerprint(
|
|
||||||
name = "buildPlayerRequestURIFingerprint",
|
|
||||||
returnType = "Ljava/lang/String;",
|
|
||||||
strings = listOf(
|
|
||||||
"key",
|
|
||||||
"asig",
|
|
||||||
),
|
|
||||||
customFingerprint = { method, _ ->
|
|
||||||
indexOfToStringInstruction(method) >= 0
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
internal fun indexOfToStringInstruction(method: Method) =
|
|
||||||
method.indexOfFirstInstruction {
|
|
||||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
|
||||||
getReference<MethodReference>().toString() == "Landroid/net/Uri;->toString()Ljava/lang/String;"
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val buildRequestFingerprint = legacyFingerprint(
|
internal val buildRequestFingerprint = legacyFingerprint(
|
||||||
name = "buildRequestFingerprint",
|
name = "buildRequestFingerprint",
|
||||||
customFingerprint = { method, _ ->
|
customFingerprint = { method, _ ->
|
||||||
|
@ -55,4 +55,14 @@
|
|||||||
<item>6.11.52</item>
|
<item>6.11.52</item>
|
||||||
<item>4.27.53</item>
|
<item>4.27.53</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string-array name="revanced_spoof_streaming_data_type_entries">
|
||||||
|
<item>@string/revanced_spoof_streaming_data_type_entry_android_vr</item>
|
||||||
|
<item>@string/revanced_spoof_streaming_data_type_entry_ios</item>
|
||||||
|
<item>@string/revanced_spoof_streaming_data_type_entry_ios_music</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="revanced_spoof_streaming_data_type_entry_values">
|
||||||
|
<item>ANDROID_VR</item>
|
||||||
|
<item>IOS</item>
|
||||||
|
<item>IOS_MUSIC</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -452,10 +452,16 @@ Tap on the continue button and disable battery optimizations."</string>
|
|||||||
Limitations:
|
Limitations:
|
||||||
• OPUS audio codec may not be supported.
|
• OPUS audio codec may not be supported.
|
||||||
• Seekbar thumbnail may not be present.
|
• Seekbar thumbnail may not be present.
|
||||||
• Watch history does not work with a brand account."</string>
|
• Watch history does not work with a brand account.
|
||||||
|
|
||||||
|
※ When used with 'Spoofing streaming data', playback issues may occur."</string>
|
||||||
|
|
||||||
<string name="revanced_spoof_streaming_data_title">Spoof streaming data</string>
|
<string name="revanced_spoof_streaming_data_title">Spoof streaming data</string>
|
||||||
<string name="revanced_spoof_streaming_data_summary">Spoof the streaming data to prevent playback issues.</string>
|
<string name="revanced_spoof_streaming_data_summary">"Spoof the streaming data to prevent playback issues.
|
||||||
|
|
||||||
|
※ When used with 'Spoof client', playback issues may occur."</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_title">Default client</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_summary">Defines a default client that fetches streaming data.</string>
|
||||||
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">Show in Stats for nerds</string>
|
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">Show in Stats for nerds</string>
|
||||||
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary">Shows the client used to fetch streaming data in Stats for nerds.</string>
|
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary">Shows the client used to fetch streaming data in Stats for nerds.</string>
|
||||||
<string name="revanced_spoof_streaming_data_type_entry_ios">iOS</string>
|
<string name="revanced_spoof_streaming_data_type_entry_ios">iOS</string>
|
||||||
|
Reference in New Issue
Block a user