-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(YouTube): Add
Spoof client
patch
- Loading branch information
Showing
7 changed files
with
248 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
212 changes: 212 additions & 0 deletions
212
app/src/main/java/app/revanced/integrations/youtube/patches/misc/SpoofClientPatch.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
package app.revanced.integrations.youtube.patches.misc; | ||
|
||
import android.net.Uri; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
|
||
import org.apache.commons.lang3.StringUtils; | ||
|
||
import app.revanced.integrations.shared.utils.Logger; | ||
import app.revanced.integrations.youtube.settings.Settings; | ||
import app.revanced.integrations.youtube.shared.PlayerType; | ||
|
||
/** | ||
* @noinspection ALL | ||
*/ | ||
public class SpoofClientPatch { | ||
private static final boolean SPOOF_CLIENT_ENABLED = Settings.SPOOF_CLIENT.get(); | ||
private static final boolean SPOOF_CLIENT_USE_IOS = Settings.SPOOF_CLIENT_USE_IOS.get(); | ||
|
||
/** | ||
* The device machine id for the Meta Quest 3, used to get opus codec with the Android VR client. | ||
* | ||
* <p> | ||
* See <a href="https://dumps.tadiphone.dev/dumps/oculus/eureka">this GitLab</a> for more | ||
* information. | ||
* </p> | ||
*/ | ||
private static final String ANDROID_VR_DEVICE_MODEL = "Quest 3"; | ||
|
||
/** | ||
* The hardcoded client version of the Android VR 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://www.meta.com/en-us/experiences/2002317119880945/">the App | ||
* Store page of the YouTube app</a>, in the {@code Additional details} section. | ||
* </p> | ||
*/ | ||
private static final String ANDROID_VR_YOUTUBE_CLIENT_VERSION = "1.56.21"; | ||
|
||
/** | ||
* The device machine id for the iPhone 15 Pro Max, used to get 60fps with the iOS client. | ||
* | ||
* <p> | ||
* See <a href="https://gist.github.com/adamawolf/3048717">this GitHub Gist</a> for more | ||
* information. | ||
* </p> | ||
*/ | ||
private static final String IOS_DEVICE_MODEL = "iPhone16,2"; | ||
|
||
/** | ||
* 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-watch-listen-stream/id544007664/">the App | ||
* Store page of the YouTube app</a>, in the {@code What’s New} section. | ||
* </p> | ||
*/ | ||
private static final String IOS_YOUTUBE_CLIENT_VERSION = "19.20.2"; | ||
|
||
/** | ||
* Clips or Shorts Parameters. | ||
*/ | ||
private static final String[] CLIPS_OR_SHORTS_PARAMETERS = { | ||
"kAIB", // Clips | ||
"8AEB" // Shorts | ||
}; | ||
|
||
/** | ||
* iOS client is used for Clips or Shorts. | ||
*/ | ||
private static volatile boolean useIOSClient; | ||
|
||
/** | ||
* 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 a localhost URI. | ||
* | ||
* @param playerRequestUri The URI of the player request. | ||
* @return Localhost URI if the request is a /get_watch request, otherwise the original URI. | ||
*/ | ||
public static Uri blockGetWatchRequest(Uri playerRequestUri) { | ||
if (SPOOF_CLIENT_ENABLED) { | ||
try { | ||
String path = playerRequestUri.getPath(); | ||
|
||
if (path != null && path.contains("get_watch")) { | ||
Logger.printDebug(() -> "Blocking: " + playerRequestUri + " by returning: " + UNREACHABLE_HOST_URI_STRING); | ||
|
||
return UNREACHABLE_HOST_URI; | ||
} | ||
} catch (Exception ex) { | ||
Logger.printException(() -> "blockGetWatchRequest failure", ex); | ||
} | ||
} | ||
|
||
return playerRequestUri; | ||
} | ||
|
||
/** | ||
* Injection point. | ||
* | ||
* Blocks /initplayback requests. | ||
*/ | ||
public static String blockInitPlaybackRequest(String originalUrlString) { | ||
if (SPOOF_CLIENT_ENABLED) { | ||
try { | ||
Uri originalUri = Uri.parse(originalUrlString); | ||
String path = originalUri.getPath(); | ||
|
||
if (path != null && path.contains("initplayback")) { | ||
Logger.printDebug(() -> "Blocking: " + originalUrlString + " by returning unreachable url"); | ||
|
||
return UNREACHABLE_HOST_URI_STRING; | ||
} | ||
} catch (Exception ex) { | ||
Logger.printException(() -> "blockInitPlaybackRequest failure", ex); | ||
} | ||
} | ||
|
||
return originalUrlString; | ||
} | ||
|
||
private static ClientType getSpoofClientType() { | ||
if (SPOOF_CLIENT_USE_IOS || useIOSClient) { | ||
return ClientType.IOS; | ||
} | ||
return ClientType.ANDROID_VR; | ||
} | ||
|
||
/** | ||
* Injection point. | ||
*/ | ||
public static int getClientTypeId(int originalClientTypeId) { | ||
if (SPOOF_CLIENT_ENABLED) { | ||
return getSpoofClientType().id; | ||
} | ||
|
||
return originalClientTypeId; | ||
} | ||
|
||
/** | ||
* Injection point. | ||
*/ | ||
public static String getClientVersion(String originalClientVersion) { | ||
if (SPOOF_CLIENT_ENABLED) { | ||
return getSpoofClientType().version; | ||
} | ||
|
||
return originalClientVersion; | ||
} | ||
|
||
/** | ||
* Injection point. | ||
*/ | ||
public static String getClientModel(String originalClientModel) { | ||
if (SPOOF_CLIENT_ENABLED) { | ||
return getSpoofClientType().model; | ||
} | ||
|
||
return originalClientModel; | ||
} | ||
|
||
/** | ||
* Injection point. | ||
*/ | ||
public static boolean isClientSpoofingEnabled() { | ||
return SPOOF_CLIENT_ENABLED; | ||
} | ||
|
||
/** | ||
* Injection point. | ||
*/ | ||
public static String setPlayerResponseVideoId(@NonNull String videoId, @Nullable String parameters, boolean isShortAndOpeningOrPlaying) { | ||
useIOSClient = playerParameterIsClipsOrShorts(parameters); | ||
|
||
return parameters; // Return the original value since we are observing and not modifying. | ||
} | ||
|
||
/** | ||
* @return If the player parameters are for a Short or Clips. | ||
*/ | ||
private static boolean playerParameterIsClipsOrShorts(@Nullable String playerParameter) { | ||
if (PlayerType.getCurrent().isNoneOrHidden()) { | ||
return true; | ||
} | ||
|
||
return playerParameter != null && StringUtils.startsWithAny(playerParameter, CLIPS_OR_SHORTS_PARAMETERS); | ||
} | ||
|
||
private enum ClientType { | ||
ANDROID_VR(28, ANDROID_VR_DEVICE_MODEL, ANDROID_VR_YOUTUBE_CLIENT_VERSION), | ||
IOS(5, IOS_DEVICE_MODEL, IOS_YOUTUBE_CLIENT_VERSION); | ||
|
||
final int id; | ||
final String model; | ||
final String version; | ||
|
||
ClientType(int id, String model, String version) { | ||
this.id = id; | ||
this.model = model; | ||
this.version = version; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.