Skip to content

Commit

Permalink
fix(YouTube Music - Spoof client): Action bar not loading as of YouTu…
Browse files Browse the repository at this point in the history
…be Music 7.17.51
  • Loading branch information
inotia00 authored and anddea committed Dec 17, 2024
1 parent fd31d87 commit 943c288
Show file tree
Hide file tree
Showing 14 changed files with 253 additions and 154 deletions.
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
package app.revanced.extension.music.patches.misc;

import app.revanced.extension.music.settings.Settings;
import app.revanced.extension.shared.patches.BlockRequestPatch;

@SuppressWarnings("unused")
public class SpoofClientPatch {
public class SpoofClientPatch extends BlockRequestPatch {
private static final int CLIENT_ID_IOS_MUSIC = 26;

/**
* The hardcoded client version of the iOS app used for InnerTube requests with this client.
*
* <p>
* 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
* Store page of the YouTube app</a>, in the {@code What¡¯s New} section.
* </p>
* 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 = "6.21";
private static final String CLIENT_VERSION_IOS_MUSIC_7_31 = "7.31.2";

/**
* See <a href="https://gist.github.com/adamawolf/3048717">this GitHub Gist</a> for more
* information.
* </p>
* Starting with YouTube Music 7.17.51+, the litho component has been applied to the video action bar.
* <p>
* So if {@code CLIENT_VERSION_IOS_MUSIC_6_21} is used in YouTube Music 7.17.51+,
* the video action bar will not load properly.
*/
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 OS_VERSION_IOS_MUSIC = "17.7.2.21H221";
private static final String USER_AGENT_VERSION_IOS_MUSIC = "17_7_2";
Expand All @@ -31,13 +38,11 @@ public class SpoofClientPatch {
USER_AGENT_VERSION_IOS_MUSIC +
" like Mac OS X)";

private static final boolean SPOOF_CLIENT_ENABLED = Settings.SPOOF_CLIENT.get();

/**
* Injection point.
*/
public static int getClientTypeId(int originalClientTypeId) {
if (SPOOF_CLIENT_ENABLED) {
if (SPOOF_CLIENT) {
return CLIENT_ID_IOS_MUSIC;
}

Expand All @@ -48,7 +53,7 @@ public static int getClientTypeId(int originalClientTypeId) {
* Injection point.
*/
public static String getClientVersion(String originalClientVersion) {
if (SPOOF_CLIENT_ENABLED) {
if (SPOOF_CLIENT) {
return CLIENT_VERSION_IOS_MUSIC;
}

Expand All @@ -59,7 +64,7 @@ public static String getClientVersion(String originalClientVersion) {
* Injection point.
*/
public static String getClientModel(String originalClientModel) {
if (SPOOF_CLIENT_ENABLED) {
if (SPOOF_CLIENT) {
return DEVICE_MODEL_IOS_MUSIC;
}

Expand All @@ -70,7 +75,7 @@ public static String getClientModel(String originalClientModel) {
* Injection point.
*/
public static String getOsVersion(String originalOsVersion) {
if (SPOOF_CLIENT_ENABLED) {
if (SPOOF_CLIENT) {
return OS_VERSION_IOS_MUSIC;
}

Expand All @@ -81,7 +86,7 @@ public static String getOsVersion(String originalOsVersion) {
* Injection point.
*/
public static String getUserAgent(String originalUserAgent) {
if (SPOOF_CLIENT_ENABLED) {
if (SPOOF_CLIENT) {
return USER_AGENT_IOS_MUSIC;
}

Expand All @@ -92,16 +97,19 @@ public static String getUserAgent(String originalUserAgent) {
* Injection point.
*/
public static boolean isClientSpoofingEnabled() {
return SPOOF_CLIENT_ENABLED;
return SPOOF_CLIENT;
}

/**
* Injection point.
* <p>
* 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.
*/
public static boolean forceCreatePlaybackSpeedMenu(boolean original) {
if (SPOOF_CLIENT_ENABLED) {
if (SPOOF_CLIENT) {
return true;
}
return original;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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_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 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);


Expand Down Expand Up @@ -239,6 +238,7 @@ public class Settings extends BaseSettings {
SB_API_URL.key,
SETTINGS_IMPORT_EXPORT.key,
SPOOF_APP_VERSION_TARGET.key,
SPOOF_STREAMING_DATA_TYPE.key,
RETURN_YOUTUBE_USERNAME_ABOUT.key,
RETURN_YOUTUBE_USERNAME_DISPLAY_FORMAT.key,
RETURN_YOUTUBE_USERNAME_YOUTUBE_DATA_API_V3_DEVELOPER_KEY.key,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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_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.utils.ResourceUtils.getStringArray;
import static app.revanced.extension.shared.utils.StringRef.str;
Expand Down Expand Up @@ -158,7 +159,8 @@ public void onCreate(Bundle savedInstanceState) {
Logger.printDebug(() -> "Failed to find the right value: " + dataString);
}
} 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);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,19 @@ public class AppClient {
USER_AGENT_VERSION_IOS +
" like Mac OS X)";

// IOS_MUSIC
// IOS MUSIC
/**
* The hardcoded client version of the iOS app used for InnerTube requests with this client.
*
* <p>
* 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
* 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>
*/
private static final String CLIENT_VERSION_IOS_MUSIC = "7.31.2";
private static final String USER_AGENT_IOS_MUSIC = "com.google.ios.youtubemusic/" +
CLIENT_VERSION_IOS +
CLIENT_VERSION_IOS_MUSIC +
"(" +
DEVICE_MODEL_IOS +
"; U; CPU iOS " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,68 +8,14 @@
import java.nio.ByteBuffer;
import java.util.Map;

import app.revanced.extension.shared.patches.BlockRequestPatch;
import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.patches.spoof.requests.StreamingDataRequest;

@SuppressWarnings("unused")
public class SpoofStreamingDataPatch {
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;
}
public class SpoofStreamingDataPatch extends BlockRequestPatch {

/**
* Injection point.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,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 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 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.addSwitchPreference
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.indexOfModelInstruction
import app.revanced.util.fingerprint.matchOrThrow
Expand Down Expand Up @@ -48,7 +49,10 @@ val spoofClientPatch = bytecodePatch(
SPOOF_CLIENT.summary,
false,
) {
dependsOn(settingsPatch)
dependsOn(
settingsPatch,
blockRequestPatch
)

compatibleWith(COMPATIBLE_PACKAGE)

Expand Down Expand Up @@ -211,6 +215,8 @@ val spoofClientPatch = bytecodePatch(

// endregion

// region fix for playback speed menu is not available in Podcasts

playbackSpeedBottomSheetFingerprint.mutableClassOrThrow().let {
val onItemClickMethod =
it.methods.find { method -> method.name == "onItemClick" }
Expand Down Expand Up @@ -244,6 +250,8 @@ val spoofClientPatch = bytecodePatch(
}
}

// endregion

addSwitchPreference(
CategoryType.MISC,
"revanced_spoof_client",
Expand Down
Loading

0 comments on commit 943c288

Please sign in to comment.