mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-13 21:07:17 +02:00
fix(YouTube - Spoof streaming data): Change the default client to Android VR
, add iOS TV
to the selectable default clients, and add the Use Android clients only
setting https://github.com/inotia00/ReVanced_Extended/issues/2593
This commit is contained in:
parent
480d47587d
commit
89d038fdb8
@ -1,80 +0,0 @@
|
|||||||
package app.revanced.extension.shared.patches;
|
|
||||||
|
|
||||||
import static app.revanced.extension.shared.patches.PatchStatus.SpoofClient;
|
|
||||||
import static app.revanced.extension.shared.patches.PatchStatus.SpoofStreamingData;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
|
||||||
import app.revanced.extension.shared.utils.Logger;
|
|
||||||
|
|
||||||
@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 = BaseSettings.SPOOF_CLIENT.get();
|
|
||||||
|
|
||||||
private static final boolean BLOCK_REQUEST = (SpoofStreamingData() && SPOOF_STREAMING_DATA) || (SpoofClient() && SPOOF_CLIENT);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +1,18 @@
|
|||||||
package app.revanced.extension.shared.patches;
|
package app.revanced.extension.shared.patches;
|
||||||
|
|
||||||
import app.revanced.extension.shared.patches.client.AppClient.ClientType;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class PatchStatus {
|
public class PatchStatus {
|
||||||
public static boolean HideFullscreenAdsDefaultBoolean() {
|
public static boolean HideFullscreenAdsDefaultBoolean() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ClientType SpoofStreamingDataDefaultClient() {
|
|
||||||
return ClientType.IOS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean SpoofStreamingData() {
|
public static boolean SpoofStreamingData() {
|
||||||
// Replace this with true If the Spoof streaming data patch succeeds
|
// Replace this with true If the Spoof streaming data patch succeeds
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean SpoofStreamingDataAndroidOnlyDefaultBoolean() {
|
||||||
|
// Replace this with true If the Spoof streaming data patch succeeds in YouTube
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,15 @@ import android.os.Build;
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
|
|
||||||
public class AppClient {
|
public class AppClient {
|
||||||
// IOS
|
// IOS
|
||||||
|
/**
|
||||||
|
* Video not playable: Paid / Movie / Private / Age-restricted
|
||||||
|
* Note: Audio track available
|
||||||
|
*/
|
||||||
|
private static final String PACKAGE_NAME_IOS = "com.google.ios.youtube";
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*
|
*
|
||||||
@ -29,15 +36,35 @@ public class AppClient {
|
|||||||
private static final String DEVICE_MODEL_IOS = "iPhone16,2";
|
private static final String DEVICE_MODEL_IOS = "iPhone16,2";
|
||||||
private static final String OS_VERSION_IOS = "17.7.2.21H221";
|
private static final String OS_VERSION_IOS = "17.7.2.21H221";
|
||||||
private static final String USER_AGENT_VERSION_IOS = "17_7_2";
|
private static final String USER_AGENT_VERSION_IOS = "17_7_2";
|
||||||
private static final String USER_AGENT_IOS = "com.google.ios.youtube/" +
|
private static final String USER_AGENT_IOS =
|
||||||
CLIENT_VERSION_IOS +
|
iOSUserAgent(PACKAGE_NAME_IOS, CLIENT_VERSION_IOS);
|
||||||
"(" +
|
|
||||||
DEVICE_MODEL_IOS +
|
|
||||||
"; U; CPU iOS " +
|
// IOS UNPLUGGED
|
||||||
USER_AGENT_VERSION_IOS +
|
/**
|
||||||
" like Mac OS X)";
|
* Video not playable: Paid / Movie
|
||||||
|
* Note: Audio track available
|
||||||
|
*/
|
||||||
|
private static final String PACKAGE_NAME_IOS_UNPLUGGED = "com.google.ios.youtubeunplugged";
|
||||||
|
/**
|
||||||
|
* 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-tv/id1193350206/">the App
|
||||||
|
* Store page of the YouTube TV app</a>, in the {@code What’s New} section.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
private static final String CLIENT_VERSION_IOS_UNPLUGGED = "8.33";
|
||||||
|
private static final String USER_AGENT_IOS_UNPLUGGED =
|
||||||
|
iOSUserAgent(PACKAGE_NAME_IOS_UNPLUGGED, CLIENT_VERSION_IOS_UNPLUGGED);
|
||||||
|
|
||||||
|
|
||||||
// IOS MUSIC
|
// IOS MUSIC
|
||||||
|
/**
|
||||||
|
* Video not playable: All videos that can't be played on YouTube Music
|
||||||
|
*/
|
||||||
|
private static final String PACKAGE_NAME_IOS_MUSIC = "com.google.ios.youtubemusic";
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*
|
*
|
||||||
@ -47,16 +74,21 @@ public class AppClient {
|
|||||||
* Store page of the YouTube Music 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.04";
|
||||||
private static final String USER_AGENT_IOS_MUSIC = "com.google.ios.youtubemusic/" +
|
private static final String USER_AGENT_IOS_MUSIC =
|
||||||
CLIENT_VERSION_IOS_MUSIC +
|
iOSUserAgent(PACKAGE_NAME_IOS_MUSIC, CLIENT_VERSION_IOS_MUSIC);
|
||||||
"(" +
|
|
||||||
DEVICE_MODEL_IOS +
|
|
||||||
"; U; CPU iOS " +
|
|
||||||
USER_AGENT_VERSION_IOS +
|
|
||||||
" like Mac OS X)";
|
|
||||||
|
|
||||||
// ANDROID VR
|
// ANDROID VR
|
||||||
|
/**
|
||||||
|
* Video not playable: Kids
|
||||||
|
* Note: Audio track is not available
|
||||||
|
* <p>
|
||||||
|
* Package name for YouTube VR (Google DayDream): com.google.android.apps.youtube.vr (Deprecated)
|
||||||
|
* Package name for YouTube VR (Meta Quests): com.google.android.apps.youtube.vr.oculus
|
||||||
|
* Package name for YouTube VR (ByteDance Pico 4): com.google.android.apps.youtube.vr.pico
|
||||||
|
*/
|
||||||
|
private static final String PACKAGE_NAME_ANDROID_VR = "com.google.android.apps.youtube.vr.oculus";
|
||||||
/**
|
/**
|
||||||
* The hardcoded client version of the Android VR app used for InnerTube requests with this client.
|
* The hardcoded client version of the Android VR app used for InnerTube requests with this client.
|
||||||
*
|
*
|
||||||
@ -66,7 +98,7 @@ public class AppClient {
|
|||||||
* Store page of the YouTube app</a>, in the {@code Additional details} section.
|
* Store page of the YouTube app</a>, in the {@code Additional details} section.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
private static final String CLIENT_VERSION_ANDROID_VR = "1.61.47";
|
private static final String CLIENT_VERSION_ANDROID_VR = "1.61.48";
|
||||||
/**
|
/**
|
||||||
* The device machine id for the Meta Quest 3, used to get opus codec with the Android VR client.
|
* The device machine id for the Meta Quest 3, used to get opus codec with the Android VR client.
|
||||||
*
|
*
|
||||||
@ -82,19 +114,17 @@ public class AppClient {
|
|||||||
* but for some reason the build.props for the {@code Quest 3} state that the SDK version is 32.
|
* but for some reason the build.props for the {@code Quest 3} state that the SDK version is 32.
|
||||||
*/
|
*/
|
||||||
private static final String ANDROID_SDK_VERSION_ANDROID_VR = "32";
|
private static final String ANDROID_SDK_VERSION_ANDROID_VR = "32";
|
||||||
/**
|
private static final String USER_AGENT_ANDROID_VR =
|
||||||
* Package name for YouTube VR (Google DayDream): com.google.android.apps.youtube.vr (Deprecated)
|
androidUserAgent(PACKAGE_NAME_ANDROID_VR, CLIENT_VERSION_ANDROID_VR, OS_VERSION_ANDROID_VR);
|
||||||
* Package name for YouTube VR (Meta Quests): com.google.android.apps.youtube.vr.oculus
|
|
||||||
* Package name for YouTube VR (ByteDance Pico 4): com.google.android.apps.youtube.vr.pico
|
|
||||||
*/
|
|
||||||
private static final String USER_AGENT_ANDROID_VR = "com.google.android.apps.youtube.vr.oculus/" +
|
|
||||||
CLIENT_VERSION_ANDROID_VR +
|
|
||||||
" (Linux; U; Android " +
|
|
||||||
OS_VERSION_ANDROID_VR +
|
|
||||||
"; GB) gzip";
|
|
||||||
|
|
||||||
// ANDROID UNPLUGGED
|
// ANDROID UNPLUGGED
|
||||||
private static final String CLIENT_VERSION_ANDROID_UNPLUGGED = "8.49.0";
|
/**
|
||||||
|
* Video not playable: Playlists / Music
|
||||||
|
* Note: Audio track is not available
|
||||||
|
*/
|
||||||
|
private static final String PACKAGE_NAME_ANDROID_UNPLUGGED = "com.google.android.apps.youtube.unplugged";
|
||||||
|
private static final String CLIENT_VERSION_ANDROID_UNPLUGGED = "8.16.0";
|
||||||
/**
|
/**
|
||||||
* The device machine id for the Chromecast with Google TV 4K.
|
* The device machine id for the Chromecast with Google TV 4K.
|
||||||
*
|
*
|
||||||
@ -106,15 +136,47 @@ public class AppClient {
|
|||||||
private static final String DEVICE_MODEL_ANDROID_UNPLUGGED = "Google TV Streamer";
|
private static final String DEVICE_MODEL_ANDROID_UNPLUGGED = "Google TV Streamer";
|
||||||
private static final String OS_VERSION_ANDROID_UNPLUGGED = "14";
|
private static final String OS_VERSION_ANDROID_UNPLUGGED = "14";
|
||||||
private static final String ANDROID_SDK_VERSION_ANDROID_UNPLUGGED = "34";
|
private static final String ANDROID_SDK_VERSION_ANDROID_UNPLUGGED = "34";
|
||||||
private static final String USER_AGENT_ANDROID_UNPLUGGED = "com.google.android.apps.youtube.unplugged/" +
|
private static final String USER_AGENT_ANDROID_UNPLUGGED =
|
||||||
CLIENT_VERSION_ANDROID_UNPLUGGED +
|
androidUserAgent(PACKAGE_NAME_ANDROID_UNPLUGGED, CLIENT_VERSION_ANDROID_UNPLUGGED, OS_VERSION_ANDROID_UNPLUGGED);
|
||||||
" (Linux; U; Android " +
|
|
||||||
OS_VERSION_ANDROID_UNPLUGGED +
|
|
||||||
"; GB) gzip";
|
// ANDROID CREATOR
|
||||||
|
/**
|
||||||
|
* Video not playable: Livestream
|
||||||
|
* Note: Audio track is not available
|
||||||
|
*/
|
||||||
|
private static final String PACKAGE_NAME_ANDROID_CREATOR = "com.google.android.apps.youtube.creator";
|
||||||
|
private static final String CLIENT_VERSION_ANDROID_CREATOR = "24.14.101";
|
||||||
|
private static final String DEVICE_MODEL_ANDROID_CREATOR = Build.MODEL;
|
||||||
|
private static final String OS_VERSION_ANDROID_CREATOR = Build.VERSION.RELEASE;
|
||||||
|
private static final String ANDROID_SDK_VERSION_ANDROID_CREATOR = String.valueOf(Build.VERSION.SDK_INT);
|
||||||
|
private static final String USER_AGENT_ANDROID_CREATOR =
|
||||||
|
androidUserAgent(PACKAGE_NAME_ANDROID_CREATOR, CLIENT_VERSION_ANDROID_CREATOR, OS_VERSION_ANDROID_CREATOR);
|
||||||
|
|
||||||
|
|
||||||
private AppClient() {
|
private AppClient() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String androidUserAgent(String packageName, String clientVersion, String osVersion) {
|
||||||
|
return packageName +
|
||||||
|
"/" +
|
||||||
|
clientVersion +
|
||||||
|
" (Linux; U; Android " +
|
||||||
|
osVersion +
|
||||||
|
"; GB) gzip";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String iOSUserAgent(String packageName, String clientVersion) {
|
||||||
|
return packageName +
|
||||||
|
"/" +
|
||||||
|
clientVersion +
|
||||||
|
"(" +
|
||||||
|
DEVICE_MODEL_IOS +
|
||||||
|
"; U; CPU iOS " +
|
||||||
|
USER_AGENT_VERSION_IOS +
|
||||||
|
" like Mac OS X)";
|
||||||
|
}
|
||||||
|
|
||||||
public enum ClientType {
|
public enum ClientType {
|
||||||
IOS(5,
|
IOS(5,
|
||||||
DEVICE_MODEL_IOS,
|
DEVICE_MODEL_IOS,
|
||||||
@ -140,6 +202,22 @@ public class AppClient {
|
|||||||
CLIENT_VERSION_ANDROID_UNPLUGGED,
|
CLIENT_VERSION_ANDROID_UNPLUGGED,
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
|
ANDROID_CREATOR(14,
|
||||||
|
DEVICE_MODEL_ANDROID_CREATOR,
|
||||||
|
OS_VERSION_ANDROID_CREATOR,
|
||||||
|
USER_AGENT_ANDROID_CREATOR,
|
||||||
|
ANDROID_SDK_VERSION_ANDROID_CREATOR,
|
||||||
|
CLIENT_VERSION_ANDROID_CREATOR,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
IOS_UNPLUGGED(33,
|
||||||
|
DEVICE_MODEL_IOS,
|
||||||
|
OS_VERSION_IOS,
|
||||||
|
USER_AGENT_IOS_UNPLUGGED,
|
||||||
|
null,
|
||||||
|
CLIENT_VERSION_IOS_UNPLUGGED,
|
||||||
|
true
|
||||||
|
),
|
||||||
IOS_MUSIC(
|
IOS_MUSIC(
|
||||||
26,
|
26,
|
||||||
DEVICE_MODEL_IOS,
|
DEVICE_MODEL_IOS,
|
||||||
@ -208,8 +286,28 @@ public class AppClient {
|
|||||||
this.canLogin = canLogin;
|
this.canLogin = canLogin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final ClientType[] CLIENT_ORDER_TO_USE_ANDROID = {
|
||||||
|
ANDROID_VR,
|
||||||
|
ANDROID_UNPLUGGED,
|
||||||
|
ANDROID_CREATOR,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final ClientType[] CLIENT_ORDER_TO_USE_DEFAULT = {
|
||||||
|
IOS,
|
||||||
|
ANDROID_VR,
|
||||||
|
ANDROID_UNPLUGGED,
|
||||||
|
IOS_UNPLUGGED,
|
||||||
|
IOS_MUSIC,
|
||||||
|
};
|
||||||
|
|
||||||
public final String getFriendlyName() {
|
public final String getFriendlyName() {
|
||||||
return getString("revanced_spoof_streaming_data_type_entry_" + name().toLowerCase());
|
return getString("revanced_spoof_streaming_data_type_entry_" + name().toLowerCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ClientType[] getAvailableClientTypes() {
|
||||||
|
return BaseSettings.SPOOF_STREAMING_DATA_ANDROID_ONLY.get()
|
||||||
|
? ClientType.CLIENT_ORDER_TO_USE_ANDROID
|
||||||
|
: ClientType.CLIENT_ORDER_TO_USE_DEFAULT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package app.revanced.extension.shared.patches.spoof;
|
package app.revanced.extension.shared.patches.spoof;
|
||||||
|
|
||||||
|
import static app.revanced.extension.shared.patches.PatchStatus.SpoofStreamingData;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
@ -10,14 +12,20 @@ import java.util.Collections;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import app.revanced.extension.shared.patches.BlockRequestPatch;
|
|
||||||
import app.revanced.extension.shared.patches.spoof.requests.StreamingDataRequest;
|
import app.revanced.extension.shared.patches.spoof.requests.StreamingDataRequest;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
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;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class SpoofStreamingDataPatch extends BlockRequestPatch {
|
public class SpoofStreamingDataPatch {
|
||||||
|
public static final boolean SPOOF_STREAMING_DATA = SpoofStreamingData() && 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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key: video id
|
* Key: video id
|
||||||
@ -33,6 +41,55 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
*/
|
*/
|
||||||
@ -146,7 +203,7 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch {
|
|||||||
* Called after {@link #getStreamingData(String)}.
|
* Called after {@link #getStreamingData(String)}.
|
||||||
*/
|
*/
|
||||||
public static long getApproxDurationMs(String videoId) {
|
public static long getApproxDurationMs(String videoId) {
|
||||||
if (videoId != null) {
|
if (SPOOF_STREAMING_DATA && videoId != null) {
|
||||||
final Long approxDurationMs = approxDurationMsMap.get(videoId);
|
final Long approxDurationMs = approxDurationMsMap.get(videoId);
|
||||||
if (approxDurationMs != null) {
|
if (approxDurationMs != null) {
|
||||||
Logger.printDebug(() -> "Replacing video length: " + approxDurationMs + " for videoId: " + videoId);
|
Logger.printDebug(() -> "Replacing video length: " + approxDurationMs + " for videoId: " + videoId);
|
||||||
|
@ -52,6 +52,10 @@ public final class PlayerRoutes {
|
|||||||
client.put("osVersion", clientType.osVersion);
|
client.put("osVersion", clientType.osVersion);
|
||||||
if (clientType.androidSdkVersion != null) {
|
if (clientType.androidSdkVersion != null) {
|
||||||
client.put("androidSdkVersion", clientType.androidSdkVersion);
|
client.put("androidSdkVersion", clientType.androidSdkVersion);
|
||||||
|
client.put("osName", "Android");
|
||||||
|
} else {
|
||||||
|
client.put("deviceMake", "Apple");
|
||||||
|
client.put("osName", "iOS");
|
||||||
}
|
}
|
||||||
client.put("hl", LOCALE_LANGUAGE);
|
client.put("hl", LOCALE_LANGUAGE);
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package app.revanced.extension.shared.patches.spoof.requests;
|
package app.revanced.extension.shared.patches.spoof.requests;
|
||||||
|
|
||||||
|
import static app.revanced.extension.shared.patches.client.AppClient.getAvailableClientTypes;
|
||||||
import static app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes.GET_STREAMING_DATA;
|
import static app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes.GET_STREAMING_DATA;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@ -13,6 +14,7 @@ import java.net.HttpURLConnection;
|
|||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -80,16 +82,20 @@ public class StreamingDataRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ClientType[] allClientTypes = ClientType.values();
|
ClientType[] allClientTypes = getAvailableClientTypes();
|
||||||
ClientType preferredClient = BaseSettings.SPOOF_STREAMING_DATA_TYPE.get();
|
ClientType preferredClient = BaseSettings.SPOOF_STREAMING_DATA_TYPE.get();
|
||||||
|
|
||||||
CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
|
if (Arrays.stream(allClientTypes).noneMatch(preferredClient::equals)) {
|
||||||
CLIENT_ORDER_TO_USE[0] = preferredClient;
|
CLIENT_ORDER_TO_USE = allClientTypes;
|
||||||
|
} else {
|
||||||
|
CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
|
||||||
|
CLIENT_ORDER_TO_USE[0] = preferredClient;
|
||||||
|
|
||||||
int i = 1;
|
int i = 1;
|
||||||
for (ClientType c : allClientTypes) {
|
for (ClientType c : allClientTypes) {
|
||||||
if (c != preferredClient) {
|
if (c != preferredClient) {
|
||||||
CLIENT_ORDER_TO_USE[i++] = c;
|
CLIENT_ORDER_TO_USE[i++] = c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package app.revanced.extension.shared.settings;
|
|||||||
import static java.lang.Boolean.FALSE;
|
import static java.lang.Boolean.FALSE;
|
||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
import static app.revanced.extension.shared.patches.PatchStatus.HideFullscreenAdsDefaultBoolean;
|
import static app.revanced.extension.shared.patches.PatchStatus.HideFullscreenAdsDefaultBoolean;
|
||||||
import static app.revanced.extension.shared.patches.PatchStatus.SpoofStreamingDataDefaultClient;
|
import static app.revanced.extension.shared.patches.PatchStatus.SpoofStreamingDataAndroidOnlyDefaultBoolean;
|
||||||
|
|
||||||
import app.revanced.extension.shared.patches.ReturnYouTubeUsernamePatch.DisplayFormat;
|
import app.revanced.extension.shared.patches.ReturnYouTubeUsernamePatch.DisplayFormat;
|
||||||
import app.revanced.extension.shared.patches.client.AppClient.ClientType;
|
import app.revanced.extension.shared.patches.client.AppClient.ClientType;
|
||||||
@ -37,8 +37,8 @@ public class BaseSettings {
|
|||||||
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);
|
||||||
|
|
||||||
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", ClientType.ANDROID_VR, true);
|
||||||
public static final BooleanSetting SPOOF_STREAMING_DATA_SYNC_VIDEO_LENGTH = new BooleanSetting("revanced_spoof_streaming_data_sync_video_length", TRUE, true);
|
public static final BooleanSetting SPOOF_STREAMING_DATA_ANDROID_ONLY = new BooleanSetting("revanced_spoof_streaming_data_android_only", SpoofStreamingDataAndroidOnlyDefaultBoolean(), true, "revanced_spoof_streaming_data_android_only_user_dialog_message");
|
||||||
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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
package app.revanced.extension.youtube.settings.preference;
|
||||||
|
|
||||||
|
import static app.revanced.extension.shared.utils.ResourceUtils.getStringArray;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.ListPreference;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.patches.client.AppClient.ClientType;
|
||||||
|
import app.revanced.extension.shared.settings.EnumSetting;
|
||||||
|
import app.revanced.extension.shared.settings.Setting;
|
||||||
|
import app.revanced.extension.shared.utils.Utils;
|
||||||
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
|
public class SpoofStreamingDataDefaultClientListPreference extends ListPreference {
|
||||||
|
|
||||||
|
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
|
||||||
|
// Because this listener may run before the ReVanced settings fragment updates Settings,
|
||||||
|
// this could show the prior config and not the current.
|
||||||
|
//
|
||||||
|
// Push this call to the end of the main run queue,
|
||||||
|
// so all other listeners are done and Settings is up to date.
|
||||||
|
Utils.runOnMainThread(this::updateUI);
|
||||||
|
};
|
||||||
|
|
||||||
|
public SpoofStreamingDataDefaultClientListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpoofStreamingDataDefaultClientListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpoofStreamingDataDefaultClientListPreference(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpoofStreamingDataDefaultClientListPreference(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addChangeListener() {
|
||||||
|
Setting.preferences.preferences.registerOnSharedPreferenceChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeChangeListener() {
|
||||||
|
Setting.preferences.preferences.unregisterOnSharedPreferenceChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
|
||||||
|
super.onAttachedToHierarchy(preferenceManager);
|
||||||
|
updateUI();
|
||||||
|
addChangeListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPrepareForRemoval() {
|
||||||
|
super.onPrepareForRemoval();
|
||||||
|
removeChangeListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateUI() {
|
||||||
|
final boolean spoofStreamingDataAndroidOnly = Settings.SPOOF_STREAMING_DATA_ANDROID_ONLY.get();
|
||||||
|
final String entryKey = spoofStreamingDataAndroidOnly
|
||||||
|
? "revanced_spoof_streaming_data_type_android_entries"
|
||||||
|
: "revanced_spoof_streaming_data_type_android_ios_entries";
|
||||||
|
final String entryValueKey = spoofStreamingDataAndroidOnly
|
||||||
|
? "revanced_spoof_streaming_data_type_android_entry_values"
|
||||||
|
: "revanced_spoof_streaming_data_type_android_ios_entry_values";
|
||||||
|
final String[] mEntries = getStringArray(entryKey);
|
||||||
|
final String[] mEntryValues = getStringArray(entryValueKey);
|
||||||
|
setEntries(mEntries);
|
||||||
|
setEntryValues(mEntryValues);
|
||||||
|
|
||||||
|
final EnumSetting<ClientType> clientType = Settings.SPOOF_STREAMING_DATA_TYPE;
|
||||||
|
final boolean isAndroid = clientType.get().name().startsWith("ANDROID");
|
||||||
|
if (spoofStreamingDataAndroidOnly && !isAndroid) {
|
||||||
|
clientType.resetToDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
setEnabled(Settings.SPOOF_STREAMING_DATA.get());
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,6 @@ import android.preference.Preference;
|
|||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
import app.revanced.extension.shared.patches.client.AppClient.ClientType;
|
|
||||||
import app.revanced.extension.shared.settings.Setting;
|
import app.revanced.extension.shared.settings.Setting;
|
||||||
import app.revanced.extension.shared.utils.Utils;
|
import app.revanced.extension.shared.utils.Utils;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
@ -63,8 +62,14 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateUI() {
|
private void updateUI() {
|
||||||
final ClientType clientType = Settings.SPOOF_STREAMING_DATA_TYPE.get();
|
final String clientName = Settings.SPOOF_STREAMING_DATA_TYPE.get().name().toLowerCase();
|
||||||
final String summaryTextKey = "revanced_spoof_streaming_data_side_effects_" + clientType.name().toLowerCase();
|
String summaryTextKey = "revanced_spoof_streaming_data_side_effects_";
|
||||||
|
|
||||||
|
if (Settings.SPOOF_STREAMING_DATA_ANDROID_ONLY.get()) {
|
||||||
|
summaryTextKey += "android";
|
||||||
|
} else {
|
||||||
|
summaryTextKey += clientName;
|
||||||
|
}
|
||||||
|
|
||||||
setSummary(str(summaryTextKey));
|
setSummary(str(summaryTextKey));
|
||||||
setEnabled(Settings.SPOOF_STREAMING_DATA.get());
|
setEnabled(Settings.SPOOF_STREAMING_DATA.get());
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package app.revanced.patches.music.utils.fix.streamingdata
|
package app.revanced.patches.music.utils.fix.streamingdata
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||||
import app.revanced.patches.music.utils.compatibility.Constants.YOUTUBE_MUSIC_PACKAGE_NAME
|
import app.revanced.patches.music.utils.compatibility.Constants.YOUTUBE_MUSIC_PACKAGE_NAME
|
||||||
import app.revanced.patches.music.utils.patch.PatchList.SPOOF_STREAMING_DATA
|
import app.revanced.patches.music.utils.patch.PatchList.SPOOF_STREAMING_DATA
|
||||||
@ -10,15 +8,8 @@ import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
|
|||||||
import app.revanced.patches.music.utils.settings.addPreferenceWithIntent
|
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.spoof.streamingdata.baseSpoofStreamingDataPatch
|
import app.revanced.patches.shared.spoof.streamingdata.baseSpoofStreamingDataPatch
|
||||||
import app.revanced.patches.shared.spoof.useragent.baseSpoofUserAgentPatch
|
import app.revanced.patches.shared.spoof.useragent.baseSpoofUserAgentPatch
|
||||||
import app.revanced.util.findMethodOrThrow
|
|
||||||
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.reference.FieldReference
|
|
||||||
|
|
||||||
private const val DEFAULT_CLIENT_TYPE = "ANDROID_VR"
|
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
|
val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
|
||||||
@ -31,17 +22,6 @@ val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
findMethodOrThrow("$PATCHES_PATH/PatchStatus;") {
|
|
||||||
name == "SpoofStreamingDataDefaultClient"
|
|
||||||
}.apply {
|
|
||||||
val register = getInstruction<OneRegisterInstruction>(0).registerA
|
|
||||||
val type = (getInstruction<ReferenceInstruction>(0).reference as FieldReference).type
|
|
||||||
replaceInstruction(
|
|
||||||
0,
|
|
||||||
"sget-object v$register, $type->$DEFAULT_CLIENT_TYPE:$type"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
addSwitchPreference(
|
addSwitchPreference(
|
||||||
CategoryType.MISC,
|
CategoryType.MISC,
|
||||||
"revanced_spoof_streaming_data",
|
"revanced_spoof_streaming_data",
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
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;"
|
|
||||||
}
|
|
||||||
|
|
@ -4,15 +4,14 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
|||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.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.removeInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
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.PatchException
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import app.revanced.patches.shared.blockrequest.blockRequestPatch
|
|
||||||
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
|
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
|
||||||
import app.revanced.patches.shared.extension.Constants.SPOOF_PATH
|
import app.revanced.patches.shared.extension.Constants.SPOOF_PATH
|
||||||
import app.revanced.patches.shared.formatStreamModelConstructorFingerprint
|
import app.revanced.patches.shared.formatStreamModelConstructorFingerprint
|
||||||
@ -45,11 +44,47 @@ 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 {
|
||||||
@ -154,7 +189,7 @@ fun baseSpoofStreamingDataPatch(
|
|||||||
"$resultMethodType->$setStreamDataMethodName($videoDetailsClass)V",
|
"$resultMethodType->$setStreamDataMethodName($videoDetailsClass)V",
|
||||||
)
|
)
|
||||||
|
|
||||||
resultClassDef.methods.add(
|
result.classDef.methods.add(
|
||||||
ImmutableMethod(
|
ImmutableMethod(
|
||||||
resultMethodType,
|
resultMethodType,
|
||||||
setStreamDataMethodName,
|
setStreamDataMethodName,
|
||||||
|
@ -15,6 +15,37 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|||||||
const val STREAMING_DATA_INTERFACE =
|
const val STREAMING_DATA_INTERFACE =
|
||||||
"Lcom/google/protos/youtube/api/innertube/StreamingDataOuterClass${'$'}StreamingData;"
|
"Lcom/google/protos/youtube/api/innertube/StreamingDataOuterClass${'$'}StreamingData;"
|
||||||
|
|
||||||
|
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;"
|
||||||
|
}
|
||||||
|
|
||||||
internal val buildMediaDataSourceFingerprint = legacyFingerprint(
|
internal val buildMediaDataSourceFingerprint = legacyFingerprint(
|
||||||
name = "buildMediaDataSourceFingerprint",
|
name = "buildMediaDataSourceFingerprint",
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package app.revanced.patches.youtube.utils.fix.streamingdata
|
package app.revanced.patches.youtube.utils.fix.streamingdata
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import app.revanced.patches.shared.extension.Constants.PATCHES_PATH
|
||||||
import app.revanced.patches.shared.spoof.streamingdata.baseSpoofStreamingDataPatch
|
import app.revanced.patches.shared.spoof.streamingdata.baseSpoofStreamingDataPatch
|
||||||
import app.revanced.patches.shared.spoof.useragent.baseSpoofUserAgentPatch
|
import app.revanced.patches.shared.spoof.useragent.baseSpoofUserAgentPatch
|
||||||
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||||
@ -7,6 +9,7 @@ import app.revanced.patches.youtube.utils.compatibility.Constants.YOUTUBE_PACKAG
|
|||||||
import app.revanced.patches.youtube.utils.patch.PatchList.SPOOF_STREAMING_DATA
|
import app.revanced.patches.youtube.utils.patch.PatchList.SPOOF_STREAMING_DATA
|
||||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
|
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
|
||||||
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||||
|
import app.revanced.util.findMethodOrThrow
|
||||||
|
|
||||||
val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
|
val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
|
||||||
{
|
{
|
||||||
@ -18,6 +21,13 @@ val spoofStreamingDataPatch = baseSpoofStreamingDataPatch(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
findMethodOrThrow("$PATCHES_PATH/PatchStatus;") {
|
||||||
|
name == "SpoofStreamingDataAndroidOnlyDefaultBoolean"
|
||||||
|
}.replaceInstruction(
|
||||||
|
0,
|
||||||
|
"const/4 v0, 0x1"
|
||||||
|
)
|
||||||
|
|
||||||
addPreference(
|
addPreference(
|
||||||
arrayOf(
|
arrayOf(
|
||||||
"SETTINGS: SPOOF_STREAMING_DATA"
|
"SETTINGS: SPOOF_STREAMING_DATA"
|
||||||
|
@ -235,13 +235,23 @@
|
|||||||
<item>HEART_TINT</item>
|
<item>HEART_TINT</item>
|
||||||
<item>HIDDEN</item>
|
<item>HIDDEN</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="revanced_spoof_streaming_data_type_entries">
|
<string-array name="revanced_spoof_streaming_data_type_android_entries">
|
||||||
<item>@string/revanced_spoof_streaming_data_type_entry_ios</item>
|
|
||||||
<item>@string/revanced_spoof_streaming_data_type_entry_android_unplugged</item>
|
<item>@string/revanced_spoof_streaming_data_type_entry_android_unplugged</item>
|
||||||
<item>@string/revanced_spoof_streaming_data_type_entry_android_vr</item>
|
<item>@string/revanced_spoof_streaming_data_type_entry_android_vr</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="revanced_spoof_streaming_data_type_entry_values">
|
<string-array name="revanced_spoof_streaming_data_type_android_entry_values">
|
||||||
|
<item>ANDROID_UNPLUGGED</item>
|
||||||
|
<item>ANDROID_VR</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="revanced_spoof_streaming_data_type_android_ios_entries">
|
||||||
|
<item>@string/revanced_spoof_streaming_data_type_entry_ios</item>
|
||||||
|
<item>@string/revanced_spoof_streaming_data_type_entry_ios_unplugged</item>
|
||||||
|
<item>@string/revanced_spoof_streaming_data_type_entry_android_unplugged</item>
|
||||||
|
<item>@string/revanced_spoof_streaming_data_type_entry_android_vr</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="revanced_spoof_streaming_data_type_android_ios_entry_values">
|
||||||
<item>IOS</item>
|
<item>IOS</item>
|
||||||
|
<item>IOS_UNPLUGGED</item>
|
||||||
<item>ANDROID_UNPLUGGED</item>
|
<item>ANDROID_UNPLUGGED</item>
|
||||||
<item>ANDROID_VR</item>
|
<item>ANDROID_VR</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
@ -1914,14 +1914,20 @@ Tap the continue button and allow optimization changes."</string>
|
|||||||
<string name="revanced_spoof_streaming_data_type_title">Default client</string>
|
<string name="revanced_spoof_streaming_data_type_title">Default client</string>
|
||||||
<string name="revanced_spoof_streaming_data_type_entry_ios">iOS</string>
|
<string name="revanced_spoof_streaming_data_type_entry_ios">iOS</string>
|
||||||
<string name="revanced_spoof_streaming_data_type_entry_ios_music">iOS Music</string>
|
<string name="revanced_spoof_streaming_data_type_entry_ios_music">iOS Music</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_entry_ios_unplugged">iOS TV</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_entry_android_creator">Android Creator</string>
|
||||||
<string name="revanced_spoof_streaming_data_type_entry_android_unplugged">Android TV</string>
|
<string name="revanced_spoof_streaming_data_type_entry_android_unplugged">Android TV</string>
|
||||||
<string name="revanced_spoof_streaming_data_type_entry_android_vr">Android VR</string>
|
<string name="revanced_spoof_streaming_data_type_entry_android_vr">Android VR</string>
|
||||||
<string name="revanced_spoof_streaming_data_side_effects_title">Spoofing side effects</string>
|
<string name="revanced_spoof_streaming_data_side_effects_title">Spoofing side effects</string>
|
||||||
<string name="revanced_spoof_streaming_data_side_effects_ios">• Not yet found.</string>
|
<string name="revanced_spoof_streaming_data_side_effects_ios">• Not yet found.</string>
|
||||||
<string name="revanced_spoof_streaming_data_side_effects_android_unplugged">"• Audio track menu is missing.
|
<string name="revanced_spoof_streaming_data_side_effects_ios_unplugged">• Movies or paid videos may not play.</string>
|
||||||
• Stable volume is not available."</string>
|
<string name="revanced_spoof_streaming_data_side_effects_android">"• Audio track menu is missing.
|
||||||
<string name="revanced_spoof_streaming_data_side_effects_android_vr">"• Audio track menu is missing.
|
• Stable volume is not available.
|
||||||
• Stable volume is not available."</string>
|
• Disable forced auto audio tracks is not available."</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_android_only_title">Use Android clients only</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_android_only_summary_on">Android clients are used to fetch streaming data.</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_android_only_summary_off">Android and iOS clients are used to fetch streaming data.</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_android_only_user_dialog_message">Turning off this setting may cause video playback issues.</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_on">Client used to fetch streaming data is shown in Stats for nerds.</string>
|
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">Client used to fetch streaming data is shown in Stats for nerds.</string>
|
||||||
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">Client used to fetch streaming data is hidden in Stats for nerds.</string>
|
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">Client used to fetch streaming data is hidden in Stats for nerds.</string>
|
||||||
|
@ -791,8 +791,9 @@
|
|||||||
<!-- SETTINGS: SPOOF_STREAMING_DATA
|
<!-- SETTINGS: SPOOF_STREAMING_DATA
|
||||||
<PreferenceScreen android:title="@string/revanced_preference_screen_spoof_streaming_data_title" android:key="revanced_preference_screen_spoof_streaming_data" android:summary="@string/revanced_preference_screen_spoof_streaming_data_summary">
|
<PreferenceScreen android:title="@string/revanced_preference_screen_spoof_streaming_data_title" android:key="revanced_preference_screen_spoof_streaming_data" android:summary="@string/revanced_preference_screen_spoof_streaming_data_summary">
|
||||||
<SwitchPreference android:title="@string/revanced_spoof_streaming_data_title" android:key="revanced_spoof_streaming_data" android:summaryOn="@string/revanced_spoof_streaming_data_summary_on" android:summaryOff="@string/revanced_spoof_streaming_data_summary_off" />
|
<SwitchPreference android:title="@string/revanced_spoof_streaming_data_title" android:key="revanced_spoof_streaming_data" android:summaryOn="@string/revanced_spoof_streaming_data_summary_on" android:summaryOff="@string/revanced_spoof_streaming_data_summary_off" />
|
||||||
<ListPreference android:entries="@array/revanced_spoof_streaming_data_type_entries" android:title="@string/revanced_spoof_streaming_data_type_title" android:key="revanced_spoof_streaming_data_type" android:entryValues="@array/revanced_spoof_streaming_data_type_entry_values" android:dependency="revanced_spoof_streaming_data" />
|
<app.revanced.extension.youtube.settings.preference.SpoofStreamingDataDefaultClientListPreference android:title="@string/revanced_spoof_streaming_data_type_title" android:key="revanced_spoof_streaming_data_type" />
|
||||||
<app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference android:title="@string/revanced_spoof_streaming_data_side_effects_title" />
|
<app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference android:title="@string/revanced_spoof_streaming_data_side_effects_title" />
|
||||||
|
<SwitchPreference android:title="@string/revanced_spoof_streaming_data_android_only_title" android:key="revanced_spoof_streaming_data_android_only" android:summaryOn="@string/revanced_spoof_streaming_data_android_only_summary_on" android:summaryOff="@string/revanced_spoof_streaming_data_android_only_summary_off" android:dependency="revanced_spoof_streaming_data" />
|
||||||
<SwitchPreference android:title="@string/revanced_spoof_streaming_data_stats_for_nerds_title" android:key="revanced_spoof_streaming_data_stats_for_nerds" android:summaryOn="@string/revanced_spoof_streaming_data_stats_for_nerds_summary_on" android:summaryOff="@string/revanced_spoof_streaming_data_stats_for_nerds_summary_off" android:dependency="revanced_spoof_streaming_data" />
|
<SwitchPreference android:title="@string/revanced_spoof_streaming_data_stats_for_nerds_title" android:key="revanced_spoof_streaming_data_stats_for_nerds" android:summaryOn="@string/revanced_spoof_streaming_data_stats_for_nerds_summary_on" android:summaryOff="@string/revanced_spoof_streaming_data_stats_for_nerds_summary_off" android:dependency="revanced_spoof_streaming_data" />
|
||||||
</PreferenceScreen>SETTINGS: SPOOF_STREAMING_DATA -->
|
</PreferenceScreen>SETTINGS: SPOOF_STREAMING_DATA -->
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user