diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java index 318c0d971..181d0607a 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java @@ -5,13 +5,9 @@ import android.text.TextUtils; import androidx.annotation.Nullable; -import com.google.protos.youtube.api.innertube.StreamingDataOuterClass$StreamingData; - -import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.util.Collections; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import app.revanced.extension.shared.patches.BlockRequestPatch; @@ -22,12 +18,6 @@ import app.revanced.extension.shared.utils.Utils; @SuppressWarnings("unused") public class SpoofStreamingDataPatch extends BlockRequestPatch { - /** - * Even if the default client is not iOS, videos that cannot be played on Android VR or Android TV will fall back to iOS. - * Do not add a dependency that checks whether the default client is iOS or not. - */ - private static final boolean SPOOF_STREAMING_DATA_SYNC_VIDEO_LENGTH = - SPOOF_STREAMING_DATA && BaseSettings.SPOOF_STREAMING_DATA_SYNC_VIDEO_LENGTH.get(); /** * Key: video id @@ -133,29 +123,13 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch { *
* Called after {@link #getStreamingData(String)}. */ - public static void setApproxDurationMs(String videoId, String approxDurationMsFieldName, - StreamingDataOuterClass$StreamingData originalStreamingData, StreamingDataOuterClass$StreamingData spoofedStreamingData) { - if (SPOOF_STREAMING_DATA_SYNC_VIDEO_LENGTH) { - if (formatsIsEmpty(spoofedStreamingData)) { - List> originalFormats = getFormatsFromStreamingData(originalStreamingData); - Long approxDurationMs = getApproxDurationMs(originalFormats, approxDurationMsFieldName); - if (approxDurationMs != null) { - approxDurationMsMap.put(videoId, approxDurationMs); - Logger.printDebug(() -> "New approxDurationMs loaded, video id: " + videoId + ", video length: " + approxDurationMs); - } else { - Logger.printDebug(() -> "Ignoring as original approxDurationMs is not found, video id: " + videoId); - } - } else { - Logger.printDebug(() -> "Ignoring as spoofed formats is not empty, video id: " + videoId); - } + public static void setApproxDurationMs(String videoId, long approxDurationMs) { + if (approxDurationMs != Long.MAX_VALUE) { + approxDurationMsMap.put(videoId, approxDurationMs); + Logger.printDebug(() -> "New approxDurationMs loaded, video id: " + videoId + ", video length: " + approxDurationMs); } } - /** - * Looks like the initial value for the videoId field. - */ - private static final String MASKED_VIDEO_ID = "zzzzzzzzzzz"; - /** * Injection point. *
@@ -171,22 +145,16 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch { *
* Called after {@link #getStreamingData(String)}.
*/
- public static long getApproxDurationMsFromOriginalResponse(String videoId, long lengthMilliseconds) {
- if (SPOOF_STREAMING_DATA_SYNC_VIDEO_LENGTH) {
- try {
- if (videoId != null && !videoId.equals(MASKED_VIDEO_ID)) {
- Long approxDurationMs = approxDurationMsMap.get(videoId);
- if (approxDurationMs != null) {
- Logger.printDebug(() -> "Replacing video length from " + lengthMilliseconds + " to " + approxDurationMs + " , videoId: " + videoId);
- approxDurationMsMap.remove(videoId);
- return approxDurationMs;
- }
- }
- } catch (Exception ex) {
- Logger.printException(() -> "getOriginalFormats failure", ex);
+ public static long getApproxDurationMs(String videoId) {
+ if (videoId != null) {
+ final Long approxDurationMs = approxDurationMsMap.get(videoId);
+ if (approxDurationMs != null) {
+ Logger.printDebug(() -> "Replacing video length: " + approxDurationMs + " for videoId: " + videoId);
+ approxDurationMsMap.remove(videoId);
+ return approxDurationMs;
}
}
- return lengthMilliseconds;
+ return Long.MAX_VALUE;
}
/**
@@ -228,47 +196,4 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch {
return videoFormat;
}
-
- // Utils
-
- private static boolean formatsIsEmpty(StreamingDataOuterClass$StreamingData streamingData) {
- List> formats = getFormatsFromStreamingData(streamingData);
- return formats == null || formats.size() == 0;
- }
-
- private static List> getFormatsFromStreamingData(StreamingDataOuterClass$StreamingData streamingData) {
- try {
- // Field e: 'formats'.
- // Field name is always 'e', regardless of the client version.
- Field field = streamingData.getClass().getDeclaredField("e");
- field.setAccessible(true);
- if (field.get(streamingData) instanceof List> list) {
- return list;
- }
- } catch (NoSuchFieldException | IllegalAccessException ex) {
- Logger.printException(() -> "Reflection error accessing formats", ex);
- }
- return null;
- }
-
- private static Long getApproxDurationMs(List> list, String approxDurationMsFieldName) {
- try {
- if (list != null) {
- var iterator = list.listIterator();
- if (iterator.hasNext()) {
- var formats = iterator.next();
- Field field = formats.getClass().getDeclaredField(approxDurationMsFieldName);
- field.setAccessible(true);
- if (field.get(formats) instanceof Long approxDurationMs) {
- return approxDurationMs;
- } else {
- Logger.printDebug(() -> "Field type is null: " + approxDurationMsFieldName);
- }
- }
- }
- } catch (NoSuchFieldException | IllegalAccessException ex) {
- Logger.printException(() -> "Reflection error accessing field: " + approxDurationMsFieldName, ex);
- }
- return null;
- }
}
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java
index e6e27a972..92eec6bcf 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java
@@ -64,10 +64,7 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
private void updateUI() {
final ClientType clientType = Settings.SPOOF_STREAMING_DATA_TYPE.get();
- final String summaryTextKey = clientType == ClientType.IOS &&
- !Settings.SPOOF_STREAMING_DATA_SYNC_VIDEO_LENGTH.get()
- ? "revanced_spoof_streaming_data_side_effects_ios_skip_sync_video_length"
- : "revanced_spoof_streaming_data_side_effects_" + clientType.name().toLowerCase();
+ final String summaryTextKey = "revanced_spoof_streaming_data_side_effects_" + clientType.name().toLowerCase();
setSummary(str(summaryTextKey));
setEnabled(Settings.SPOOF_STREAMING_DATA.get());
diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatch.kt
index 811e1c9b6..86723635b 100644
--- a/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/shared/spoof/streamingdata/BaseSpoofStreamingDataPatch.kt
@@ -4,6 +4,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
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.replaceInstruction
import app.revanced.patcher.patch.BytecodePatchBuilder
@@ -22,7 +23,7 @@ import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
-import app.revanced.util.indexOfFirstInstructionReversedOrThrow
+import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
@@ -37,10 +38,6 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
const val EXTENSION_CLASS_DESCRIPTOR =
"$SPOOF_PATH/SpoofStreamingDataPatch;"
-// In YouTube 17.34.36, this class is obfuscated.
-const val STREAMING_DATA_INTERFACE =
- "Lcom/google/protos/youtube/api/innertube/StreamingDataOuterClass${'$'}StreamingData;"
-
fun baseSpoofStreamingDataPatch(
block: BytecodePatchBuilder.() -> Unit = {},
executeBlock: BytecodePatchContext.() -> Unit = {},
@@ -84,18 +81,51 @@ fun baseSpoofStreamingDataPatch(
// region Replace the streaming data.
- val approxDurationMsFieldName = formatStreamModelConstructorFingerprint.matchOrThrow().let {
- with(it.method) {
- val approxDurationMsFieldIndex = it.patternMatch!!.startIndex
- (getInstruction