diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java
index 604679e61..4016c8ef9 100644
--- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java
+++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java
@@ -6,7 +6,6 @@ import androidx.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.Map;
-import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
@@ -87,14 +86,22 @@ public class SpoofVideoStreamsPatch {
try {
Uri uri = Uri.parse(url);
String path = uri.getPath();
+
// 'heartbeat' has no video id and appears to be only after playback has started.
- if (path != null && path.contains("player") && !path.contains("heartbeat")) {
- String videoId = Objects.requireNonNull(uri.getQueryParameter("id"));
- StreamingDataRequest.fetchRequest(videoId, requestHeaders);
+ // 'refresh' has no video id and appears to happen when waiting for a livestream to start.
+ if (path != null && path.contains("player") && !path.contains("heartbeat")
+ && !path.contains("refresh")) {
+ String id = uri.getQueryParameter("id");
+ if (id == null) {
+ Logger.printException(() -> "Ignoring request that has no video id." +
+ " Url: " + url + " headers: " + requestHeaders);
+ return;
+ }
+
+ StreamingDataRequest.fetchRequest(id, requestHeaders);
}
} catch (Exception ex) {
- Logger.printException(() -> "buildRequest failure. Url: " + url
- + " headers: " + requestHeaders, ex);
+ Logger.printException(() -> "buildRequest failure", ex);
}
}
}
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java
new file mode 100644
index 000000000..4a9be0d5f
--- /dev/null
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java
@@ -0,0 +1,85 @@
+package app.revanced.extension.youtube.settings.preference;
+
+import static app.revanced.extension.shared.StringRef.str;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.Preference;
+import android.preference.PreferenceManager;
+import android.util.AttributeSet;
+
+import androidx.annotation.Nullable;
+
+import app.revanced.extension.shared.Logger;
+import app.revanced.extension.shared.Utils;
+import app.revanced.extension.shared.settings.BaseSettings;
+import app.revanced.extension.shared.settings.Setting;
+import app.revanced.extension.shared.spoof.ClientType;
+
+@SuppressWarnings({"deprecation", "unused"})
+public class SpoofStreamingDataSideEffectsPreference extends Preference {
+
+ @Nullable
+ private ClientType currentClientType;
+
+ 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 SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SpoofStreamingDataSideEffectsPreference(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() {
+ ClientType clientType = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
+ if (currentClientType == clientType) {
+ return;
+ }
+
+ Logger.printDebug(() -> "Updating spoof stream side effects preference");
+ setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get());
+
+ String key = "revanced_spoof_video_streams_about_"
+ + clientType.name().toLowerCase();
+ setTitle(str(key + "_title"));
+ setSummary(str(key + "_summary"));
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt
index 7d740b1c0..45bdfc0c8 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt
@@ -45,8 +45,11 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
summaryKey = null
),
SwitchPreference("revanced_spoof_video_streams_ios_force_avc"),
- NonInteractivePreference("revanced_spoof_video_streams_about_android_vr"),
- NonInteractivePreference("revanced_spoof_video_streams_about_ios"),
+ // Preference requires a title but the actual text is chosen at runtime.
+ NonInteractivePreference(
+ key = "revanced_spoof_video_streams_about_android_vr",
+ tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference"
+ ),
),
),
)
diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml
index c3154cd6d..b8cb89820 100644
--- a/patches/src/main/resources/addresources/values/strings.xml
+++ b/patches/src/main/resources/addresources/values/strings.xml
@@ -1229,11 +1229,10 @@ This is because Crowdin requires temporarily flattening this file and removing t
Video codec is determined automatically
Enabling this might improve battery life and fix playback stuttering.\n\nAVC has a maximum resolution of 1080p, Opus audio codec is not available, and video playback will use more internet data than VP9 or AV1.
iOS spoofing side effects
- • Private kids videos may not play\n• Livestreams start from the beginning\n• Videos may end 1 second early
+ • Private kids videos may not play\n• Age restricted videos may not play\n• Livestreams start from the beginning\n• Videos end 1 second early
Android VR spoofing side effects
- • Kids videos may not play\n• Audio track menu is missing\n• Stable volume is not available
- Video streams are spoofed
- Preferred audio stream language
+ • Kids videos may not play\n• Age restricted videos may not play\n• Livestreams start from the beginning\n• Videos end 1 second early
+ Default audio stream language
App language
Arabic
Azerbaijani