diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/SeekbarThumbnailsPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/SeekbarThumbnailsPatch.java
new file mode 100644
index 0000000000..a48274336c
--- /dev/null
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/SeekbarThumbnailsPatch.java
@@ -0,0 +1,16 @@
+package app.revanced.extension.youtube.patches;
+
+import app.revanced.extension.youtube.settings.Settings;
+
+@SuppressWarnings("unused")
+public class SeekbarThumbnailsPatch {
+
+ private static final boolean SEEKBAR_THUMBNAILS_HIGH_QUALITY_ENABLED = Settings.SEEKBAR_THUMBNAILS_HIGH_QUALITY.get();
+
+ /**
+ * Injection point.
+ */
+ public static boolean useHighQualityFullscreenThumbnails() {
+ return SEEKBAR_THUMBNAILS_HIGH_QUALITY_ENABLED;
+ }
+}
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java
index 1ff26a7ba7..d463cbdd52 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java
@@ -250,6 +250,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", TRUE);
public static final BooleanSetting SLIDE_TO_SEEK = new BooleanSetting("revanced_slide_to_seek", FALSE, true);
public static final BooleanSetting RESTORE_OLD_SEEKBAR_THUMBNAILS = new BooleanSetting("revanced_restore_old_seekbar_thumbnails", TRUE);
+ public static final BooleanSetting SEEKBAR_THUMBNAILS_HIGH_QUALITY = new BooleanSetting("revanced_seekbar_thumbnails_high_quality", FALSE, true, "revanced_seekbar_thumbnails_high_quality_dialog_message");
public static final BooleanSetting HIDE_SEEKBAR = new BooleanSetting("revanced_hide_seekbar", FALSE, true);
public static final BooleanSetting HIDE_SEEKBAR_THUMBNAIL = new BooleanSetting("revanced_hide_seekbar_thumbnail", FALSE);
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
diff --git a/patches/api/patches.api b/patches/api/patches.api
index 6d6f8d2762..4782cf765a 100644
--- a/patches/api/patches.api
+++ b/patches/api/patches.api
@@ -1196,6 +1196,10 @@ public final class app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch
public static final fun getSeekbarColorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
+public final class app/revanced/patches/youtube/layout/seekbar/SeekbarThumbnailsPatchKt {
+ public static final fun getSeekbarThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
+}
+
public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatchKt {
public static final fun getShortsAutoplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/Fingerprints.kt
index cf620bc644..c3fe10c2dd 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/Fingerprints.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/Fingerprints.kt
@@ -117,3 +117,10 @@ internal val slideToSeekFingerprint = fingerprint {
)
literal { 67108864 }
}
+
+internal val fullscreenSeekbarThumbnailsQualityFingerprint = fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Z")
+ parameters()
+ literal { 45399684L }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatch.kt
new file mode 100644
index 0000000000..9a49d0e012
--- /dev/null
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatch.kt
@@ -0,0 +1,64 @@
+package app.revanced.patches.youtube.layout.seekbar
+
+import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
+import app.revanced.patcher.patch.bytecodePatch
+import app.revanced.patches.all.misc.resources.addResources
+import app.revanced.patches.all.misc.resources.addResourcesPatch
+import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
+import app.revanced.patches.youtube.interaction.seekbar.fullscreenSeekbarThumbnailsQualityFingerprint
+import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
+import app.revanced.patches.youtube.misc.playservice.is_19_17_or_greater
+import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
+import app.revanced.patches.youtube.misc.settings.PreferenceScreen
+
+private const val EXTENSION_CLASS_DESCRIPTOR =
+ "Lapp/revanced/extension/youtube/patches/SeekbarThumbnailsPatch;"
+
+@Suppress("unused")
+val seekbarThumbnailsPatch = bytecodePatch(
+ name = "Seekbar thumbnails",
+ description = "Adds an option to use high quality fullscreen seekbar thumbnails.",
+) {
+ dependsOn(
+ sharedExtensionPatch,
+ addResourcesPatch,
+ versionCheckPatch,
+ )
+
+ compatibleWith(
+ "com.google.android.youtube"(
+ "18.38.44",
+ "18.49.37",
+ "19.16.39",
+ "19.25.37",
+ "19.34.42",
+ )
+ )
+
+ val fullscreenSeekbarThumbnailsQualityMatch by fullscreenSeekbarThumbnailsQualityFingerprint()
+
+ execute {
+ addResources("youtube", "layout.seekbar.seekbarThumbnailsPatch")
+
+ PreferenceScreen.SEEKBAR.addPreferences(
+ if (!is_19_17_or_greater) {
+ SwitchPreference(
+ key = "revanced_seekbar_thumbnails_high_quality",
+ summaryOnKey = "revanced_seekbar_thumbnails_high_quality_legacy_summary_on",
+ summaryOffKey = "revanced_seekbar_thumbnails_high_quality_legacy_summary_on"
+ )
+ } else {
+ SwitchPreference("revanced_seekbar_thumbnails_high_quality")
+ }
+ )
+
+ fullscreenSeekbarThumbnailsQualityMatch.mutableMethod.addInstructions(
+ 0,
+ """
+ invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->useHighQualityFullscreenThumbnails()Z
+ move-result v0
+ return v0
+ """
+ )
+ }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch.kt
index a21927289a..1dbdad8845 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch.kt
@@ -2,7 +2,6 @@ package app.revanced.patches.youtube.layout.seekbar
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
-import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -11,6 +10,7 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_17_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
+import java.util.logging.Logger
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/RestoreOldSeekbarThumbnailsPatch;"
@@ -39,8 +39,7 @@ val restoreOldSeekbarThumbnailsPatch = bytecodePatch(
execute {
if (is_19_17_or_greater) {
- // Give a more informative error, if the user has turned off version checks.
- throw PatchException("'Restore old seekbar thumbnails' cannot be patched to any version after 19.16.39")
+ return@execute Logger.getLogger(this::class.java.name).severe("'Restore old seekbar thumbnails' cannot be patched to any version after 19.16.39")
}
addResources("youtube", "layout.seekbar.restoreOldSeekbarThumbnailsPatch")
diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml
index 75b7a65409..d1fdfd8c81 100644
--- a/patches/src/main/resources/addresources/values/strings.xml
+++ b/patches/src/main/resources/addresources/values/strings.xml
@@ -762,6 +762,14 @@ This is because Crowdin requires temporarily flattening this file and removing t
Wide search bar is enabled
Wide search bar is disabled
+
+ Enable high quality thumbnails
+ Seekbar thumbnails are high quality
+ Seekbar thumbnails are medium quality
+ Fullscreen seekbar thumbnails are high quality
+ Fullscreen seekbar thumbnails are medium quality
+ This will restore thumbnails to livestreams that do not have seekbar thumbnails.\n\nInternet data usage may be higher, and seekbar thumbnails will have a slight delay before showing.\n\nThis feature works best with a very fast internet connection.
+
Restore old seekbar thumbnails
Seekbar thumbnails will appear above the seekbar