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