feat(YouTube - Alternative thumbnails): Selectively enable for home / subscription / search

This commit is contained in:
LisoUseInAIKyrios 2024-03-27 18:26:22 +04:00
parent 194f9ca3e9
commit 88ccb9f58f
3 changed files with 99 additions and 121 deletions

View File

@ -5,9 +5,13 @@ import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.integrations.shared.settings.Setting;
import app.revanced.integrations.youtube.settings.Settings;
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.shared.Utils;
import app.revanced.integrations.youtube.shared.NavigationBar;
import app.revanced.integrations.youtube.shared.PlayerType;
import org.chromium.net.UrlRequest;
import org.chromium.net.UrlResponseInfo;
import org.chromium.net.impl.CronetUrlRequest;
@ -21,6 +25,9 @@ import java.util.Map;
import java.util.concurrent.ExecutionException;
import static app.revanced.integrations.shared.StringRef.str;
import static app.revanced.integrations.youtube.settings.Settings.ALT_THUMBNAIL_HOME;
import static app.revanced.integrations.youtube.settings.Settings.ALT_THUMBNAIL_SEARCH;
import static app.revanced.integrations.youtube.settings.Settings.ALT_THUMBNAIL_SUBSCRIPTIONS;
/**
* Alternative YouTube thumbnails.
@ -49,6 +56,61 @@ import static app.revanced.integrations.shared.StringRef.str;
@SuppressWarnings("unused")
public final class AlternativeThumbnailsPatch {
// These must be class declarations if declared here,
// otherwise the app will not load due to cyclic initialization errors.
public static final class DeArrowAvailability implements Setting.Availability {
public static boolean usingDeArrowAnywhere() {
return ALT_THUMBNAIL_HOME.get().useDeArrow
|| ALT_THUMBNAIL_SUBSCRIPTIONS.get().useDeArrow
|| ALT_THUMBNAIL_SEARCH.get().useDeArrow;
}
@Override
public boolean isAvailable() {
return usingDeArrowAnywhere();
}
}
public static final class StillImagesAvailability implements Setting.Availability {
public static boolean usingStillImagesAnywhere() {
return ALT_THUMBNAIL_HOME.get().useStillImages
|| ALT_THUMBNAIL_SUBSCRIPTIONS.get().useStillImages
|| ALT_THUMBNAIL_SEARCH.get().useStillImages;
}
@Override
public boolean isAvailable() {
return usingStillImagesAnywhere();
}
}
public enum ThumbnailOption {
ORIGINAL(false, false),
DEARROW(true, false),
DEARROW_STILL_IMAGES(true, true),
STILL_IMAGES(false, true);
public final boolean useDeArrow;
public final boolean useStillImages;
ThumbnailOption(boolean useDeArrow, boolean useStillImages) {
this.useDeArrow = useDeArrow;
this.useStillImages = useStillImages;
}
}
public enum ThumbnailStillTime {
BEGINNING(1),
MIDDLE(2),
END(3);
private final int urlSuffix;
ThumbnailStillTime(int urlSuffix) {
this.urlSuffix = urlSuffix;
}
}
private static final Uri dearrowApiUri;
/**
@ -78,13 +140,6 @@ public final class AlternativeThumbnailsPatch {
* Fix any bad imported data.
*/
private static Uri validateSettings() {
final int altThumbnailType = Settings.ALT_THUMBNAIL_STILLS_TIME.get();
if (altThumbnailType < 1 || altThumbnailType > 3) {
Utils.showToastLong("Invalid Alternative still thumbnail type: "
+ altThumbnailType + ". Using default");
Settings.ALT_THUMBNAIL_STILLS_TIME.resetToDefault();
}
Uri apiUri = Uri.parse(Settings.ALT_THUMBNAIL_DEARROW_API_URL.get());
// Cannot use unsecured 'http', otherwise the connections fail to start and no callbacks hooks are made.
String scheme = apiUri.getScheme();
@ -96,12 +151,17 @@ public final class AlternativeThumbnailsPatch {
return apiUri;
}
private static boolean usingDeArrow() {
return Settings.ALT_THUMBNAIL_DEARROW.get();
}
private static boolean usingVideoStills() {
return Settings.ALT_THUMBNAIL_STILLS.get();
private static ThumbnailOption thumbnailOptionForCurrentNavigation() {
if (NavigationBar.isSearchBarActive()) { // Must check search first.
return ALT_THUMBNAIL_SEARCH.get();
}
if (NavigationBar.NavigationButton.HOME.isSelected()
|| PlayerType.getCurrent().isMaximizedOrFullscreen()) {
return ALT_THUMBNAIL_HOME.get();
}
// User is in the subscription, notification, or the library tab.
// For now, also change the history tab thumbnails if subscriptions are enabled.
return ALT_THUMBNAIL_SUBSCRIPTIONS.get();
}
/**
@ -179,9 +239,8 @@ public final class AlternativeThumbnailsPatch {
*/
public static String overrideImageURL(String originalUrl) {
try {
final boolean usingDeArrow = usingDeArrow();
final boolean usingVideoStills = usingVideoStills();
if (!usingDeArrow && !usingVideoStills) {
ThumbnailOption option = thumbnailOptionForCurrentNavigation();
if (option == ThumbnailOption.ORIGINAL) {
return originalUrl;
}
@ -200,14 +259,14 @@ public final class AlternativeThumbnailsPatch {
String sanitizedReplacementUrl;
final boolean includeTracking;
if (usingDeArrow && canUseDeArrowAPI()) {
if (option.useDeArrow && canUseDeArrowAPI()) {
includeTracking = false; // Do not include view tracking parameters with API call.
final String fallbackUrl = usingVideoStills
final String fallbackUrl = option.useStillImages
? buildYoutubeVideoStillURL(decodedUrl, qualityToUse)
: decodedUrl.sanitizedUrl;
sanitizedReplacementUrl = buildDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl);
} else if (usingVideoStills) {
} else if (option.useStillImages) {
includeTracking = true; // Include view tracking parameters if present.
sanitizedReplacementUrl = buildYoutubeVideoStillURL(decodedUrl, qualityToUse);
} else {
@ -240,7 +299,7 @@ public final class AlternativeThumbnailsPatch {
String url = responseInfo.getUrl();
if (usingDeArrow() && urlIsDeArrow(url)) {
if (urlIsDeArrow(url)) {
Logger.printDebug(() -> "handleCronetSuccess, statusCode: " + statusCode);
if (statusCode == 304) {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304
@ -250,7 +309,7 @@ public final class AlternativeThumbnailsPatch {
return;
}
if (usingVideoStills() && statusCode == 404) {
if (statusCode == 404) {
// Fast alt thumbnails is enabled and the thumbnail is not available.
// The video is:
// - live stream
@ -294,15 +353,13 @@ public final class AlternativeThumbnailsPatch {
@Nullable UrlResponseInfo responseInfo,
IOException exception) {
try {
if (usingDeArrow()) {
String url = ((CronetUrlRequest) request).getHookedUrl();
if (urlIsDeArrow(url)) {
Logger.printDebug(() -> "handleCronetFailure, exception: " + exception);
final int statusCode = (responseInfo != null)
? responseInfo.getHttpStatusCode()
: 0;
handleDeArrowError(url, statusCode);
}
String url = ((CronetUrlRequest) request).getHookedUrl();
if (urlIsDeArrow(url)) {
Logger.printDebug(() -> "handleCronetFailure, exception: " + exception);
final int statusCode = (responseInfo != null)
? responseInfo.getHttpStatusCode()
: 0;
handleDeArrowError(url, statusCode);
}
} catch (Exception ex) {
Logger.printException(() -> "Callback failure error", ex);
@ -398,7 +455,7 @@ public final class AlternativeThumbnailsPatch {
}
String getAltImageNameToUse() {
return altImageName + Settings.ALT_THUMBNAIL_STILLS_TIME.get();
return altImageName + Settings.ALT_THUMBNAIL_STILLS_TIME.get().urlSuffix;
}
}

View File

@ -3,6 +3,10 @@ package app.revanced.integrations.youtube.settings;
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.shared.settings.*;
import app.revanced.integrations.shared.settings.preference.SharedPrefCategory;
import app.revanced.integrations.youtube.patches.AlternativeThumbnailsPatch.DeArrowAvailability;
import app.revanced.integrations.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability;
import app.revanced.integrations.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption;
import app.revanced.integrations.youtube.patches.AlternativeThumbnailsPatch.ThumbnailStillTime;
import app.revanced.integrations.youtube.patches.spoof.SpoofAppVersionPatch;
import app.revanced.integrations.youtube.sponsorblock.SponsorBlockSettings;
@ -52,13 +56,14 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_VIDEO_ADS = new BooleanSetting("revanced_hide_video_ads", TRUE, true);
public static final BooleanSetting HIDE_WEB_SEARCH_RESULTS = new BooleanSetting("revanced_hide_web_search_results", TRUE);
// Layout
public static final BooleanSetting ALT_THUMBNAIL_STILLS = new BooleanSetting("revanced_alt_thumbnail_stills", FALSE);
public static final IntegerSetting ALT_THUMBNAIL_STILLS_TIME = new IntegerSetting("revanced_alt_thumbnail_stills_time", 2, parent(ALT_THUMBNAIL_STILLS));
public static final BooleanSetting ALT_THUMBNAIL_STILLS_FAST = new BooleanSetting("revanced_alt_thumbnail_stills_fast", FALSE, parent(ALT_THUMBNAIL_STILLS));
public static final BooleanSetting ALT_THUMBNAIL_DEARROW = new BooleanSetting("revanced_alt_thumbnail_dearrow", FALSE);
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_dearrow_home", ThumbnailOption.ORIGINAL);
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_SUBSCRIPTIONS = new EnumSetting<>("revanced_alt_thumbnail_dearrow_subscription", ThumbnailOption.ORIGINAL);
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_SEARCH = new EnumSetting<>("revanced_alt_thumbnail_dearrow_search", ThumbnailOption.ORIGINAL);
public static final StringSetting ALT_THUMBNAIL_DEARROW_API_URL = new StringSetting("revanced_alt_thumbnail_dearrow_api_url",
"https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true, parent(ALT_THUMBNAIL_DEARROW));
public static final BooleanSetting ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST = new BooleanSetting("revanced_alt_thumbnail_dearrow_connection_toast", TRUE, parent(ALT_THUMBNAIL_DEARROW));
"https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true, new DeArrowAvailability());
public static final BooleanSetting ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST = new BooleanSetting("revanced_alt_thumbnail_dearrow_connection_toast", TRUE, new DeArrowAvailability());
public static final EnumSetting<ThumbnailStillTime> ALT_THUMBNAIL_STILLS_TIME = new EnumSetting<>("revanced_alt_thumbnail_stills_time", ThumbnailStillTime.MIDDLE, new StillImagesAvailability());
public static final BooleanSetting ALT_THUMBNAIL_STILLS_FAST = new BooleanSetting("revanced_alt_thumbnail_stills_fast", FALSE, new StillImagesAvailability());
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);

View File

@ -1,84 +0,0 @@
package app.revanced.integrations.youtube.settings.preference;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import app.revanced.integrations.shared.Logger;
import app.revanced.integrations.shared.Utils;
import app.revanced.integrations.shared.settings.Setting;
import app.revanced.integrations.youtube.settings.Settings;
import static app.revanced.integrations.shared.StringRef.str;
/**
* Shows what thumbnails will be used based on the current settings.
*/
@SuppressWarnings("unused")
public class AlternativeThumbnailsStatusPreference extends Preference {
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
// Because this listener may run before the ReVanced settings fragment updates SettingsEnum,
// 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 SettingsEnum is up to date.
Utils.runOnMainThread(this::updateUI);
};
public AlternativeThumbnailsStatusPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public AlternativeThumbnailsStatusPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public AlternativeThumbnailsStatusPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AlternativeThumbnailsStatusPreference(Context context) {
super(context);
}
private void addChangeListener() {
Logger.printDebug(() -> "addChangeListener");
Setting.preferences.preferences.registerOnSharedPreferenceChangeListener(listener);
}
private void removeChangeListener() {
Logger.printDebug(() -> "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() {
Logger.printDebug(() -> "updateUI");
final boolean usingDeArrow = Settings.ALT_THUMBNAIL_DEARROW.get();
final boolean usingVideoStills = Settings.ALT_THUMBNAIL_STILLS.get();
final String summaryTextKey;
if (usingDeArrow && usingVideoStills) {
summaryTextKey = "revanced_alt_thumbnail_about_status_dearrow_stills";
} else if (usingDeArrow) {
summaryTextKey = "revanced_alt_thumbnail_about_status_dearrow";
} else if (usingVideoStills) {
summaryTextKey = "revanced_alt_thumbnail_about_status_stills";
} else {
summaryTextKey = "revanced_alt_thumbnail_about_status_disabled";
}
setSummary(str(summaryTextKey));
}
}