fix(YouTube - Hook download actions, Overlay buttons): Auth key is set before the Context is initialized

This commit is contained in:
inotia00 2025-04-01 19:09:19 +09:00
parent 4a19a960c5
commit 783e366242
6 changed files with 96 additions and 61 deletions

View File

@ -1,6 +1,10 @@
package app.revanced.extension.youtube.patches.utils;
import static app.revanced.extension.shared.utils.StringRef.str;
import static app.revanced.extension.shared.utils.Utils.runOnMainThreadDelayed;
import static app.revanced.extension.youtube.utils.VideoUtils.dismissPlayer;
import static app.revanced.extension.youtube.utils.VideoUtils.launchVideoExternalDownloader;
import static app.revanced.extension.youtube.utils.VideoUtils.openPlaylist;
import android.content.Context;
import android.view.KeyEvent;
@ -32,30 +36,18 @@ import app.revanced.extension.youtube.patches.utils.requests.SavePlaylistRequest
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
import app.revanced.extension.youtube.shared.VideoInformation;
import app.revanced.extension.youtube.utils.AuthUtils;
import app.revanced.extension.youtube.utils.ExtendedUtils;
import app.revanced.extension.youtube.utils.VideoUtils;
import kotlin.Pair;
// TODO: Implement sync queue and clean up code.
@SuppressWarnings({"unused", "StaticFieldLeak"})
public class PlaylistPatch extends VideoUtils {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String[] REQUEST_HEADER_KEYS = {
AUTHORIZATION_HEADER,
"X-GOOG-API-FORMAT-VERSION",
"X-Goog-Visitor-Id"
};
public class PlaylistPatch extends AuthUtils {
private static final boolean QUEUE_MANAGER =
Settings.OVERLAY_BUTTON_EXTERNAL_DOWNLOADER_QUEUE_MANAGER.get()
|| Settings.OVERRIDE_VIDEO_DOWNLOAD_BUTTON_QUEUE_MANAGER.get();
private static Context mContext;
private static volatile String authorization = "";
public static volatile String dataSyncId = "";
public static volatile boolean isIncognito = false;
private static volatile Map<String, String> requestHeader;
private static volatile String playlistId = "";
private static volatile String videoId = "";
private static String checkFailedAuth;
private static String checkFailedPlaylistId;
@ -141,30 +133,6 @@ public class PlaylistPatch extends VideoUtils {
}
}
/**
* Injection point.
*/
public static void setRequestHeaders(String url, Map<String, String> requestHeaders) {
if (QUEUE_MANAGER) {
try {
// Save requestHeaders whenever an account is switched.
String auth = requestHeaders.get(AUTHORIZATION_HEADER);
if (auth == null || authorization.equals(auth)) {
return;
}
for (String key : REQUEST_HEADER_KEYS) {
if (requestHeaders.get(key) == null) {
return;
}
}
authorization = auth;
requestHeader = requestHeaders;
} catch (Exception ex) {
Logger.printException(() -> "setRequestHeaders failure", ex);
}
}
}
/**
* Invoked by extension.
*/

View File

@ -0,0 +1,40 @@
package app.revanced.extension.youtube.utils;
import java.util.Map;
import app.revanced.extension.shared.utils.Logger;
@SuppressWarnings("unused")
public class AuthUtils {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String[] REQUEST_HEADER_KEYS = {
AUTHORIZATION_HEADER,
"X-GOOG-API-FORMAT-VERSION",
"X-Goog-Visitor-Id"
};
public static volatile String authorization = "";
public static volatile String dataSyncId = "";
public static volatile boolean isIncognito = false;
public static volatile Map<String, String> requestHeader;
public static volatile String playlistId = "";
public static volatile String videoId = "";
public static void setRequestHeaders(String url, Map<String, String> requestHeaders) {
try {
// Save requestHeaders whenever an account is switched.
String auth = requestHeaders.get(AUTHORIZATION_HEADER);
if (auth == null || authorization.equals(auth)) {
return;
}
for (String key : REQUEST_HEADER_KEYS) {
if (requestHeaders.get(key) == null) {
return;
}
}
authorization = auth;
requestHeader = requestHeaders;
} catch (Exception ex) {
Logger.initializationException(AuthUtils.class, "setRequestHeaders failure", ex);
}
}
}

View File

@ -0,0 +1,34 @@
package app.revanced.patches.youtube.utils.auth
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.youtube.utils.extension.Constants.EXTENSION_PATH
import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch
import app.revanced.patches.youtube.utils.request.buildRequestPatch
import app.revanced.patches.youtube.utils.request.hookBuildRequest
import app.revanced.util.fingerprint.methodOrThrow
private const val EXTENSION_AUTH_UTILS_CLASS_DESCRIPTOR =
"$EXTENSION_PATH/utils/AuthUtils;"
val authHookPatch = bytecodePatch(
description = "authHookPatch"
) {
dependsOn(
sharedExtensionPatch,
buildRequestPatch,
)
execute {
// Get incognito status and data sync id.
accountIdentityFingerprint.methodOrThrow().addInstructions(
1, """
sput-object p3, $EXTENSION_AUTH_UTILS_CLASS_DESCRIPTOR->dataSyncId:Ljava/lang/String;
sput-boolean p4, $EXTENSION_AUTH_UTILS_CLASS_DESCRIPTOR->isIncognito:Z
"""
)
// Get the header to use the auth token.
hookBuildRequest("$EXTENSION_AUTH_UTILS_CLASS_DESCRIPTOR->setRequestHeaders(Ljava/lang/String;Ljava/util/Map;)V")
}
}

View File

@ -0,0 +1,14 @@
package app.revanced.patches.youtube.utils.auth
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
internal val accountIdentityFingerprint = legacyFingerprint(
name = "accountIdentityFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
customFingerprint = { method, _ ->
method.definingClass.endsWith("${'$'}AutoValue_AccountIdentity;")
}
)

View File

@ -9,15 +9,6 @@ import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal val accountIdentityFingerprint = legacyFingerprint(
name = "accountIdentityFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
customFingerprint = { method, _ ->
method.definingClass.endsWith("${'$'}AutoValue_AccountIdentity;")
}
)
internal val editPlaylistConstructorFingerprint = legacyFingerprint(
name = "editPlaylistConstructorFingerprint",
returnType = "V",

View File

@ -7,13 +7,12 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.mainactivity.getMainActivityMethod
import app.revanced.patches.youtube.utils.auth.authHookPatch
import app.revanced.patches.youtube.utils.dismiss.dismissPlayerHookPatch
import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH
import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch
import app.revanced.patches.youtube.utils.mainactivity.mainActivityResolvePatch
import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.utils.request.buildRequestPatch
import app.revanced.patches.youtube.utils.request.hookBuildRequest
import app.revanced.patches.youtube.video.information.videoInformationPatch
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow
@ -34,21 +33,10 @@ val playlistPatch = bytecodePatch(
dismissPlayerHookPatch,
playerTypeHookPatch,
videoInformationPatch,
buildRequestPatch,
authHookPatch,
)
execute {
// In Incognito mode, sending a request always seems to fail.
accountIdentityFingerprint.methodOrThrow().addInstructions(
1, """
sput-object p3, $EXTENSION_CLASS_DESCRIPTOR->dataSyncId:Ljava/lang/String;
sput-boolean p4, $EXTENSION_CLASS_DESCRIPTOR->isIncognito:Z
"""
)
// Get the header to use the auth token.
hookBuildRequest("$EXTENSION_CLASS_DESCRIPTOR->setRequestHeaders(Ljava/lang/String;Ljava/util/Map;)V")
// Open the queue manager by pressing and holding the back button.
getMainActivityMethod("onKeyLongPress")
.addInstructionsWithLabels(