From fca67db5b2db919ceafc802e0d7183134344a382 Mon Sep 17 00:00:00 2001 From: shadow578 <52449218+shadow578@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:34:41 +0100 Subject: [PATCH 01/37] add dearrow option to alternative thumbnails --- .../patches/AlternativeThumbnailsPatch.java | 175 +++++++++++++++--- .../integrations/settings/SettingsEnum.java | 7 +- 2 files changed, 157 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 1e4d3f0500..c84d419d2a 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -1,5 +1,7 @@ package app.revanced.integrations.patches; +import android.net.Uri; + import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -314,8 +316,73 @@ private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEn } } + /** + * patch operation modes, see patch in revanced-patches + */ + private enum AlternativeThumbnailMode { + /** + * use thumbnails provided by the content creator. + * This should effectively disable the patch, as this options matches the stock behaviour. + */ + CREATOR_PROVIDED(1), + + /** + * use video stills provided by youtube. + * revanced_alt_thumbnail_type and revanced_alt_thumbnail_fast_quality controls what still should be used + */ + VIDEO_STILLS(2), + + /** + * use thumbnails provided by DeArrow, fallback to {@link AlternativeThumbnailMode#CREATOR_PROVIDED} + */ + DEARROW_OR_CREATOR_PROVIDED(3), + + /** + * use thumbnails provided by DeArrow, fallback to {@link AlternativeThumbnailMode#VIDEO_STILLS} + */ + DEARROW_OR_VIDEO_STILLS(4); + + private final int id; + AlternativeThumbnailMode(int id) { + this.id = id; + } + + public boolean isDeArrow() { + return this == DEARROW_OR_CREATOR_PROVIDED || this == DEARROW_OR_VIDEO_STILLS; + } + + @Nullable + public static AlternativeThumbnailMode byId(int id) { + for (final var mode : AlternativeThumbnailMode.values()) { + if(mode.id == id) { + return mode; + } + } + + return null; + } + + @NonNull + public static AlternativeThumbnailMode getCurrent() { + var mode = byId(SettingsEnum.ALT_THUMBNAIL_MODE.getInt()); + + if (mode == null) { + // fallback to stock behaviour + mode = CREATOR_PROVIDED; + } + + return mode; + } + } + static { // Fix any bad imported data. + final int altThumbnailMode = SettingsEnum.ALT_THUMBNAIL_MODE.getInt(); + if(altThumbnailMode < 1 || altThumbnailMode > 3) { + LogHelper.printException(() -> "Invalid alt thumbnail mode: " + altThumbnailMode); + SettingsEnum.ALT_THUMBNAIL_MODE.saveValue(SettingsEnum.ALT_THUMBNAIL_MODE.defaultValue); + } + final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_TYPE.getInt(); if (altThumbnailType < 1 || altThumbnailType > 3) { LogHelper.printException(() -> "Invalid alt thumbnail type: " + altThumbnailType); @@ -323,6 +390,67 @@ private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEn } } + /** + * get the alternative thumbnail url using builtin beginning / middle / end thumbnails + * + * @param decodedUrl decoded original thumbnail request url + * @return the alternative thumbnail url, or the original url. Both without tracking parameters + */ + @NonNull + private static String getYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) { + ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); + if (qualityToUse == null) return decodedUrl.sanitizedUrl; // Video is a short. + + // Images could be upgraded to webp if they are not already, but this fails quite often, + // especially for new videos uploaded in the last hour. + // And even if alt webp images do exist, sometimes they can load much slower than the original jpg alt images. + // (as much as 4x slower has been observed, despite the alt webp image being a smaller file). + + StringBuilder builder = new StringBuilder(decodedUrl.sanitizedUrl.length() + 2); + builder.append(decodedUrl.urlPrefix); + builder.append(decodedUrl.videoId).append('/'); + builder.append(qualityToUse.getAltImageNameToUse()); + builder.append('.').append(decodedUrl.imageExtension); + + String sanitizedReplacement = builder.toString(); + if (!VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { + return decodedUrl.sanitizedUrl; + } + + return builder.toString(); + } + + @Nullable + private static final Uri dearrowApiUri; + static { + dearrowApiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); + } + + /** + * get the alternative thumbnail url using DeArrow thumbnail cache + * + * @param videoId id of the video to get a thumbnail of + * @param fallbackUrl url to fallback to in case + * @return the alternative thumbnail url, without tracking parameters + */ + @NonNull + private static String getDeArrowThumbnailURL(String videoId, String fallbackUrl) { + // use fallback if parsing api uri failed + if (dearrowApiUri == null) { + return fallbackUrl; + } + + // build thumbnail request url + // see https://github.com/ajayyy/DeArrowThumbnailCache/blob/29eb4359ebdf823626c79d944a901492d760bbbc/app.py#L29 + return dearrowApiUri + .buildUpon() + .appendQueryParameter("videoID", videoId) + .appendQueryParameter("officialTime", "true") + .appendQueryParameter("redirectUrl", fallbackUrl) + .build() + .toString(); + } + /** * Injection point. Called off the main thread and by multiple threads at the same time. * @@ -330,10 +458,12 @@ private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEn */ public static String overrideImageURL(String originalUrl) { try { - if (!SettingsEnum.ALT_THUMBNAIL.getBoolean()) { + final var mode = AlternativeThumbnailMode.getCurrent(); + if (mode == AlternativeThumbnailMode.CREATOR_PROVIDED) { return originalUrl; } - DecodedThumbnailUrl decodedUrl = DecodedThumbnailUrl.decodeImageUrl(originalUrl); + + final var decodedUrl = DecodedThumbnailUrl.decodeImageUrl(originalUrl); if (decodedUrl == null) { return originalUrl; // Not a thumbnail. } @@ -341,31 +471,31 @@ public static String overrideImageURL(String originalUrl) { // Keep any tracking parameters out of the logs, and log only the base url. LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); - ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); - if (qualityToUse == null) return originalUrl; // Video is a short. - - // Images could be upgraded to webp if they are not already, but this fails quite often, - // especially for new videos uploaded in the last hour. - // And even if alt webp images do exist, sometimes they can load much slower than the original jpg alt images. - // (as much as 4x slower has been observed, despite the alt webp image being a smaller file). + // get alternative thumbnail from youtube builtin + final var videoStillUrl = getYoutubeVideoStillURL(decodedUrl); - StringBuilder builder = new StringBuilder(originalUrl.length() + 2); - builder.append(decodedUrl.urlPrefix); - builder.append(decodedUrl.videoId).append('/'); - builder.append(qualityToUse.getAltImageNameToUse()); - builder.append('.').append(decodedUrl.imageExtension); - - String sanitizedReplacement = builder.toString(); - if (!VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { - return originalUrl; + // initialize thumbnail url builder + final StringBuilder thumbnailUrlBuilder; + if(mode.isDeArrow()) { + // figure out fallback url to use + final var fallbackUrl = mode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS ? videoStillUrl : decodedUrl.sanitizedUrl; + final var deArrowUrl = getDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); + thumbnailUrlBuilder = new StringBuilder(deArrowUrl); + } else { + thumbnailUrlBuilder = new StringBuilder(videoStillUrl); } - LogHelper.printDebug(() -> "Replaced url: " + sanitizedReplacement); + // log replaced url + final var thumbnailUrlForLog = thumbnailUrlBuilder.toString(); + LogHelper.printDebug(() -> "Replaced url: " + thumbnailUrlForLog); // URL tracking parameters. Presumably they are to determine if a user has viewed a thumbnail. // This likely is used for recommendations, so they are retained if present. - builder.append(decodedUrl.urlTrackingParameters); - return builder.toString(); + if (!mode.isDeArrow()) { + thumbnailUrlBuilder.append(decodedUrl.urlTrackingParameters); + } + + return thumbnailUrlBuilder.toString(); } catch (Exception ex) { LogHelper.printException(() -> "Alt thumbnails failure", ex); return originalUrl; @@ -379,7 +509,8 @@ public static String overrideImageURL(String originalUrl) { */ public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { try { - if (responseInfo.getHttpStatusCode() == 404 && SettingsEnum.ALT_THUMBNAIL.getBoolean()) { + // 404 and alt thumbnails is using video stills + if (responseInfo.getHttpStatusCode() == 404 && AlternativeThumbnailMode.getCurrent() == AlternativeThumbnailMode.VIDEO_STILLS) { // Fast alt thumbnails is enabled and the thumbnail is not available. // The video is: // - live stream diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index e4b9550a15..319414dbba 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -56,9 +56,10 @@ public enum SettingsEnum { HIDE_WEB_SEARCH_RESULTS("revanced_hide_web_search_results", BOOLEAN, TRUE), // Layout - ALT_THUMBNAIL("revanced_alt_thumbnail", BOOLEAN, FALSE), - ALT_THUMBNAIL_TYPE("revanced_alt_thumbnail_type", INTEGER, 2, parents(ALT_THUMBNAIL)), - ALT_THUMBNAIL_FAST_QUALITY("revanced_alt_thumbnail_fast_quality", BOOLEAN, FALSE, parents(ALT_THUMBNAIL)), + ALT_THUMBNAIL_MODE("revanced_alt_thumbnail_mode", INTEGER, 1), + ALT_THUMBNAIL_TYPE("revanced_alt_thumbnail_type", INTEGER, 2), + ALT_THUMBNAIL_FAST_QUALITY("revanced_alt_thumbnail_fast_quality", BOOLEAN, FALSE), + ALT_THUMBNAIL_DEARROW_API_URL("revanced_alt_thumbnail_dearrow_api_url", STRING, "https://dearrow-thumb.ajay.app/api/v1/getThumbnail"), CUSTOM_FILTER("revanced_custom_filter", BOOLEAN, FALSE), CUSTOM_FILTER_STRINGS("revanced_custom_filter_strings", STRING, "", true, parents(CUSTOM_FILTER)), DISABLE_FULLSCREEN_AMBIENT_MODE("revanced_disable_fullscreen_ambient_mode", BOOLEAN, TRUE, true), From 25fa498e0adea512c77107e306eb81dee53a3402 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 5 Dec 2023 19:01:01 +0100 Subject: [PATCH 02/37] refactor & fix strings --- .../patches/AlternativeThumbnailsPatch.java | 352 +++++++++--------- 1 file changed, 173 insertions(+), 179 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index c84d419d2a..68646518ed 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -1,11 +1,12 @@ package app.revanced.integrations.patches; import android.net.Uri; - import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - +import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.utils.LogHelper; +import app.revanced.integrations.utils.ReVancedUtils; import org.chromium.net.UrlResponseInfo; import java.net.HttpURLConnection; @@ -15,10 +16,6 @@ import java.util.Map; import java.util.concurrent.ExecutionException; -import app.revanced.integrations.settings.SettingsEnum; -import app.revanced.integrations.utils.LogHelper; -import app.revanced.integrations.utils.ReVancedUtils; - /** * Alternative YouTube thumbnails, showing the beginning/middle/end of the video. * (ie: sd1.jpg, sd2.jpg, sd3.jpg). @@ -38,6 +35,166 @@ * This would speed up loading the watch history and users saved playlists. */ public final class AlternativeThumbnailsPatch { + @Nullable + private static final Uri dearrowApiUri; + + static { + // Fix any bad imported data. + final int mode = SettingsEnum.ALT_THUMBNAIL_MODE.getInt(); + if(mode < 1 || mode > 3) { + LogHelper.printException(() -> "Invalid alt thumbnail mode: " + mode); + SettingsEnum.ALT_THUMBNAIL_MODE.saveValue(SettingsEnum.ALT_THUMBNAIL_MODE.defaultValue); + } + + final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_TYPE.getInt(); + if (altThumbnailType < 1 || altThumbnailType > 3) { + LogHelper.printException(() -> "Invalid alt thumbnail type: " + altThumbnailType); + SettingsEnum.ALT_THUMBNAIL_TYPE.saveValue(SettingsEnum.ALT_THUMBNAIL_TYPE.defaultValue); + } + + dearrowApiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); + } + + /** + * Get the alternative thumbnail url using builtin beginning / middle / end thumbnails + * + * @param decodedUrl Decoded original thumbnail request url. + * @return The alternative thumbnail url, or the original url. Both without tracking parameters. + */ + @NonNull + private static String getYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) { + ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); + if (qualityToUse == null) return decodedUrl.sanitizedUrl; // Video is a short. + + // Images could be upgraded to webp if they are not already, but this fails quite often, + // especially for new videos uploaded in the last hour. + // And even if alt webp images do exist, sometimes they can load much slower than the original jpg alt images. + // (as much as 4x slower has been observed, despite the alt webp image being a smaller file). + + StringBuilder builder = new StringBuilder(decodedUrl.sanitizedUrl.length() + 2); + builder.append(decodedUrl.urlPrefix); + builder.append(decodedUrl.videoId).append('/'); + builder.append(qualityToUse.getAltImageNameToUse()); + builder.append('.').append(decodedUrl.imageExtension); + + String sanitizedReplacement = builder.toString(); + if (!VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { + return decodedUrl.sanitizedUrl; + } + + return builder.toString(); + } + + /** + * Get the alternative thumbnail url using DeArrow thumbnail cache. + * + * @param videoId ID of the video to get a thumbnail of. + * @param fallbackUrl URL to fall back to in case. + * @return The alternative thumbnail url, without tracking parameters. + */ + @NonNull + private static String getDeArrowThumbnailURL(String videoId, String fallbackUrl) { + // Use fallback if parsing API URI failed. + if (dearrowApiUri == null) { + return fallbackUrl; + } + + // Build thumbnail request url. + // See https://github.com/ajayyy/DeArrowThumbnailCache/blob/29eb4359ebdf823626c79d944a901492d760bbbc/app.py#L29. + return dearrowApiUri + .buildUpon() + .appendQueryParameter("videoID", videoId) + .appendQueryParameter("officialTime", "true") + .appendQueryParameter("redirectUrl", fallbackUrl) + .build() + .toString(); + } + + /** + * Injection point. Called off the main thread and by multiple threads at the same time. + * + * @param originalUrl Image url for all url images loaded, including video thumbnails. + */ + public static String overrideImageURL(String originalUrl) { + try { + final var mode = AlternativeThumbnailMode.getCurrent(); + if (mode == AlternativeThumbnailMode.ORIGINAL) { + return originalUrl; + } + + final var decodedUrl = DecodedThumbnailUrl.decodeImageUrl(originalUrl); + if (decodedUrl == null) { + return originalUrl; // Not a thumbnail. + } + + // Keep any tracking parameters out of the logs, and log only the base URL. + LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); + + // Get video still URL. + final var videoStillUrl = getYoutubeVideoStillURL(decodedUrl); + + // Initialize thumbnail URL builder. + final StringBuilder thumbnailUrlBuilder; + if(mode.isDeArrow()) { + // figure out fallback url to use + final var fallbackUrl = mode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS ? videoStillUrl : decodedUrl.sanitizedUrl; + final var deArrowUrl = getDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); + thumbnailUrlBuilder = new StringBuilder(deArrowUrl); + } else { + thumbnailUrlBuilder = new StringBuilder(videoStillUrl); + } + + // log replaced url + final var thumbnailUrlForLog = thumbnailUrlBuilder.toString(); + LogHelper.printDebug(() -> "Replaced url: " + thumbnailUrlForLog); + + // URL tracking parameters. Presumably they are to determine if a user has viewed a thumbnail. + // This likely is used for recommendations, so they are retained if present. + if (!mode.isDeArrow()) { + thumbnailUrlBuilder.append(decodedUrl.urlTrackingParameters); + } + + return thumbnailUrlBuilder.toString(); + } catch (Exception ex) { + LogHelper.printException(() -> "Alt thumbnails failure", ex); + return originalUrl; + } + } + + /** + * Injection point. + * + * Cronet considers all completed connections as a success, even if the response is 404 or 5xx. + */ + public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { + try { + // 404 and alt thumbnails is using video stills + if (responseInfo.getHttpStatusCode() == 404 && AlternativeThumbnailMode.getCurrent() == AlternativeThumbnailMode.VIDEO_STILLS) { + // Fast alt thumbnails is enabled and the thumbnail is not available. + // The video is: + // - live stream + // - upcoming unreleased video + // - very old + // - very low view count + // Take note of this, so if the image reloads the original thumbnail will be used. + DecodedThumbnailUrl decodedUrl = DecodedThumbnailUrl.decodeImageUrl(responseInfo.getUrl()); + if (decodedUrl == null) { + return; // Not a thumbnail. + } + + ThumbnailQuality quality = ThumbnailQuality.altImageNameToQuality(decodedUrl.imageQuality); + if (quality == null) { + // Video is a short or unknown quality, but the url returned 404. Should never happen. + LogHelper.printDebug(() -> "Failed to load unknown url: " + decodedUrl.sanitizedUrl); + return; + } + + VerifiedQualities.setAltThumbnailDoesNotExist(decodedUrl.videoId, quality); + } + } catch (Exception ex) { + LogHelper.printException(() -> "Alt thumbnails callback failure", ex); + } + } private enum ThumbnailQuality { // In order of lowest to highest resolution. @@ -317,28 +474,28 @@ private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEn } /** - * patch operation modes, see patch in revanced-patches + * Alternative thumbnail mode. */ private enum AlternativeThumbnailMode { /** - * use thumbnails provided by the content creator. - * This should effectively disable the patch, as this options matches the stock behaviour. + * Use the original thumbnails provided by the content creator. + * This effectively disables the patch, as this options matches the stock behaviour. */ - CREATOR_PROVIDED(1), + ORIGINAL(1), /** - * use video stills provided by youtube. - * revanced_alt_thumbnail_type and revanced_alt_thumbnail_fast_quality controls what still should be used + * Use video stills provided by YouTube. + * revanced_alt_thumbnail_type and revanced_alt_thumbnail_fast_quality controls what still should be used. */ VIDEO_STILLS(2), /** - * use thumbnails provided by DeArrow, fallback to {@link AlternativeThumbnailMode#CREATOR_PROVIDED} + * Use thumbnails provided by DeArrow, fallback to {@link AlternativeThumbnailMode#ORIGINAL}. */ DEARROW_OR_CREATOR_PROVIDED(3), /** - * use thumbnails provided by DeArrow, fallback to {@link AlternativeThumbnailMode#VIDEO_STILLS} + * Use thumbnails provided by DeArrow, fallback to {@link AlternativeThumbnailMode#VIDEO_STILLS}. */ DEARROW_OR_VIDEO_STILLS(4); @@ -367,174 +524,11 @@ public static AlternativeThumbnailMode getCurrent() { var mode = byId(SettingsEnum.ALT_THUMBNAIL_MODE.getInt()); if (mode == null) { - // fallback to stock behaviour - mode = CREATOR_PROVIDED; + // Fallback to stock behaviour. + mode = ORIGINAL; } return mode; } } - - static { - // Fix any bad imported data. - final int altThumbnailMode = SettingsEnum.ALT_THUMBNAIL_MODE.getInt(); - if(altThumbnailMode < 1 || altThumbnailMode > 3) { - LogHelper.printException(() -> "Invalid alt thumbnail mode: " + altThumbnailMode); - SettingsEnum.ALT_THUMBNAIL_MODE.saveValue(SettingsEnum.ALT_THUMBNAIL_MODE.defaultValue); - } - - final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_TYPE.getInt(); - if (altThumbnailType < 1 || altThumbnailType > 3) { - LogHelper.printException(() -> "Invalid alt thumbnail type: " + altThumbnailType); - SettingsEnum.ALT_THUMBNAIL_TYPE.saveValue(SettingsEnum.ALT_THUMBNAIL_TYPE.defaultValue); - } - } - - /** - * get the alternative thumbnail url using builtin beginning / middle / end thumbnails - * - * @param decodedUrl decoded original thumbnail request url - * @return the alternative thumbnail url, or the original url. Both without tracking parameters - */ - @NonNull - private static String getYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) { - ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); - if (qualityToUse == null) return decodedUrl.sanitizedUrl; // Video is a short. - - // Images could be upgraded to webp if they are not already, but this fails quite often, - // especially for new videos uploaded in the last hour. - // And even if alt webp images do exist, sometimes they can load much slower than the original jpg alt images. - // (as much as 4x slower has been observed, despite the alt webp image being a smaller file). - - StringBuilder builder = new StringBuilder(decodedUrl.sanitizedUrl.length() + 2); - builder.append(decodedUrl.urlPrefix); - builder.append(decodedUrl.videoId).append('/'); - builder.append(qualityToUse.getAltImageNameToUse()); - builder.append('.').append(decodedUrl.imageExtension); - - String sanitizedReplacement = builder.toString(); - if (!VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { - return decodedUrl.sanitizedUrl; - } - - return builder.toString(); - } - - @Nullable - private static final Uri dearrowApiUri; - static { - dearrowApiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); - } - - /** - * get the alternative thumbnail url using DeArrow thumbnail cache - * - * @param videoId id of the video to get a thumbnail of - * @param fallbackUrl url to fallback to in case - * @return the alternative thumbnail url, without tracking parameters - */ - @NonNull - private static String getDeArrowThumbnailURL(String videoId, String fallbackUrl) { - // use fallback if parsing api uri failed - if (dearrowApiUri == null) { - return fallbackUrl; - } - - // build thumbnail request url - // see https://github.com/ajayyy/DeArrowThumbnailCache/blob/29eb4359ebdf823626c79d944a901492d760bbbc/app.py#L29 - return dearrowApiUri - .buildUpon() - .appendQueryParameter("videoID", videoId) - .appendQueryParameter("officialTime", "true") - .appendQueryParameter("redirectUrl", fallbackUrl) - .build() - .toString(); - } - - /** - * Injection point. Called off the main thread and by multiple threads at the same time. - * - * @param originalUrl Image url for all url images loaded, including video thumbnails. - */ - public static String overrideImageURL(String originalUrl) { - try { - final var mode = AlternativeThumbnailMode.getCurrent(); - if (mode == AlternativeThumbnailMode.CREATOR_PROVIDED) { - return originalUrl; - } - - final var decodedUrl = DecodedThumbnailUrl.decodeImageUrl(originalUrl); - if (decodedUrl == null) { - return originalUrl; // Not a thumbnail. - } - - // Keep any tracking parameters out of the logs, and log only the base url. - LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); - - // get alternative thumbnail from youtube builtin - final var videoStillUrl = getYoutubeVideoStillURL(decodedUrl); - - // initialize thumbnail url builder - final StringBuilder thumbnailUrlBuilder; - if(mode.isDeArrow()) { - // figure out fallback url to use - final var fallbackUrl = mode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS ? videoStillUrl : decodedUrl.sanitizedUrl; - final var deArrowUrl = getDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); - thumbnailUrlBuilder = new StringBuilder(deArrowUrl); - } else { - thumbnailUrlBuilder = new StringBuilder(videoStillUrl); - } - - // log replaced url - final var thumbnailUrlForLog = thumbnailUrlBuilder.toString(); - LogHelper.printDebug(() -> "Replaced url: " + thumbnailUrlForLog); - - // URL tracking parameters. Presumably they are to determine if a user has viewed a thumbnail. - // This likely is used for recommendations, so they are retained if present. - if (!mode.isDeArrow()) { - thumbnailUrlBuilder.append(decodedUrl.urlTrackingParameters); - } - - return thumbnailUrlBuilder.toString(); - } catch (Exception ex) { - LogHelper.printException(() -> "Alt thumbnails failure", ex); - return originalUrl; - } - } - - /** - * Injection point. - * - * Cronet considers all completed connections as a success, even if the response is 404 or 5xx. - */ - public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { - try { - // 404 and alt thumbnails is using video stills - if (responseInfo.getHttpStatusCode() == 404 && AlternativeThumbnailMode.getCurrent() == AlternativeThumbnailMode.VIDEO_STILLS) { - // Fast alt thumbnails is enabled and the thumbnail is not available. - // The video is: - // - live stream - // - upcoming unreleased video - // - very old - // - very low view count - // Take note of this, so if the image reloads the original thumbnail will be used. - DecodedThumbnailUrl decodedUrl = DecodedThumbnailUrl.decodeImageUrl(responseInfo.getUrl()); - if (decodedUrl == null) { - return; // Not a thumbnail. - } - - ThumbnailQuality quality = ThumbnailQuality.altImageNameToQuality(decodedUrl.imageQuality); - if (quality == null) { - // Video is a short or unknown quality, but the url returned 404. Should never happen. - LogHelper.printDebug(() -> "Failed to load unknown url: " + decodedUrl.sanitizedUrl); - return; - } - - VerifiedQualities.setAltThumbnailDoesNotExist(decodedUrl.videoId, quality); - } - } catch (Exception ex) { - LogHelper.printException(() -> "Alt thumbnails callback failure", ex); - } - } - } From 424a76d8e5339a1e6e0c8aa8702c4bb9af26286c Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 5 Dec 2023 19:29:01 +0100 Subject: [PATCH 03/37] prevent network call if possible --- .../integrations/patches/AlternativeThumbnailsPatch.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 68646518ed..5d24ab47e4 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -130,17 +130,17 @@ public static String overrideImageURL(String originalUrl) { // Keep any tracking parameters out of the logs, and log only the base URL. LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); - // Get video still URL. - final var videoStillUrl = getYoutubeVideoStillURL(decodedUrl); - // Initialize thumbnail URL builder. final StringBuilder thumbnailUrlBuilder; if(mode.isDeArrow()) { // figure out fallback url to use - final var fallbackUrl = mode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS ? videoStillUrl : decodedUrl.sanitizedUrl; + final String fallbackUrl = mode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS + ? getYoutubeVideoStillURL(decodedUrl) : decodedUrl.sanitizedUrl; final var deArrowUrl = getDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); thumbnailUrlBuilder = new StringBuilder(deArrowUrl); } else { + // Get video still URL. + final var videoStillUrl = getYoutubeVideoStillURL(decodedUrl); thumbnailUrlBuilder = new StringBuilder(videoStillUrl); } From 8029a32bec72f69a38c288af967933eb02d36d10 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 5 Dec 2023 19:32:18 +0100 Subject: [PATCH 04/37] refactor --- .../patches/AlternativeThumbnailsPatch.java | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 5d24ab47e4..e9933a9d4c 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -19,7 +19,7 @@ /** * Alternative YouTube thumbnails, showing the beginning/middle/end of the video. * (ie: sd1.jpg, sd2.jpg, sd3.jpg). - * + *

* Has an additional option to use 'fast' thumbnails, * where it forces sd thumbnail quality and skips verifying if the alt thumbnail image exists. * The UI loading time will be the same or better than using the the original thumbnails, @@ -27,12 +27,13 @@ * If a failed thumbnail load is reloaded (ie: scroll off, then on screen), then the original thumbnail * is reloaded instead. Fast thumbnails requires using SD or lower thumbnail resolution, * because a noticeable number of videos do not have hq720 and too many fail to load. - * + *

* Ideas for improvements: * - Selectively allow using original thumbnails in some situations, * such as videos subscription feed, watch history, or in search results. * - Save to a temporary file the video id's verified to have alt thumbnails. * This would speed up loading the watch history and users saved playlists. + * @noinspection unused */ public final class AlternativeThumbnailsPatch { @Nullable @@ -142,19 +143,16 @@ public static String overrideImageURL(String originalUrl) { // Get video still URL. final var videoStillUrl = getYoutubeVideoStillURL(decodedUrl); thumbnailUrlBuilder = new StringBuilder(videoStillUrl); - } - - // log replaced url - final var thumbnailUrlForLog = thumbnailUrlBuilder.toString(); - LogHelper.printDebug(() -> "Replaced url: " + thumbnailUrlForLog); - // URL tracking parameters. Presumably they are to determine if a user has viewed a thumbnail. - // This likely is used for recommendations, so they are retained if present. - if (!mode.isDeArrow()) { + // URL tracking parameters. Presumably they are to determine if a user has viewed a thumbnail. + // This likely is used for recommendations, so they are retained if present. thumbnailUrlBuilder.append(decodedUrl.urlTrackingParameters); } - return thumbnailUrlBuilder.toString(); + final var thumbnailUrl = thumbnailUrlBuilder.toString(); + LogHelper.printDebug(() -> "Replaced url: " + thumbnailUrl); + + return thumbnailUrl; } catch (Exception ex) { LogHelper.printException(() -> "Alt thumbnails failure", ex); return originalUrl; @@ -163,7 +161,7 @@ public static String overrideImageURL(String originalUrl) { /** * Injection point. - * + *

* Cronet considers all completed connections as a success, even if the response is 404 or 5xx. */ public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { @@ -305,7 +303,7 @@ private static class VerifiedQualities { @Override protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > CACHE_LIMIT; // Evict oldest entry if over the cache limit. + return size() > CACHE_LIMIT; // Evict the oldest entry if over the cache limit. } }; @@ -332,6 +330,7 @@ static boolean verifyAltThumbnailExist(@NonNull String videoId, @NonNull Thumbna static void setAltThumbnailDoesNotExist(@NonNull String videoId, @NonNull ThumbnailQuality quality) { VerifiedQualities verified = getVerifiedQualities(videoId, false); + if (verified == null) return; verified.setQualityVerified(videoId, quality, false); } @@ -438,25 +437,29 @@ private static class DecodedThumbnailUrl { static DecodedThumbnailUrl decodeImageUrl(String url) { final int videoIdStartIndex = url.indexOf('/', YOUTUBE_THUMBNAIL_PREFIX.length()) + 1; if (videoIdStartIndex <= 0) return null; + final int videoIdEndIndex = url.indexOf('/', videoIdStartIndex); if (videoIdEndIndex < 0) return null; + final int imageSizeStartIndex = videoIdEndIndex + 1; final int imageSizeEndIndex = url.indexOf('.', imageSizeStartIndex); if (imageSizeEndIndex < 0) return null; + int imageExtensionEndIndex = url.indexOf('?', imageSizeEndIndex); if (imageExtensionEndIndex < 0) imageExtensionEndIndex = url.length(); + return new DecodedThumbnailUrl(url, videoIdStartIndex, videoIdEndIndex, imageSizeStartIndex, imageSizeEndIndex, imageExtensionEndIndex); } /** Full usable url, but stripped of any tracking information. */ final String sanitizedUrl; - /** Url up to the video id. */ + /** Url up to the video ID. */ final String urlPrefix; final String videoId; /** Quality, such as hq720 or sddefault. */ final String imageQuality; - /** jpg or webp */ + /** JPG or WEBP */ final String imageExtension; /** User view tracking parameters, only present on some images. */ final String urlTrackingParameters; From aaaaa95135d19e42e3dd8671220a7090029108a3 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 5 Dec 2023 19:36:17 +0100 Subject: [PATCH 05/37] restart if api url changes --- .../java/app/revanced/integrations/settings/SettingsEnum.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index 319414dbba..ef565d5e0f 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -59,7 +59,8 @@ public enum SettingsEnum { ALT_THUMBNAIL_MODE("revanced_alt_thumbnail_mode", INTEGER, 1), ALT_THUMBNAIL_TYPE("revanced_alt_thumbnail_type", INTEGER, 2), ALT_THUMBNAIL_FAST_QUALITY("revanced_alt_thumbnail_fast_quality", BOOLEAN, FALSE), - ALT_THUMBNAIL_DEARROW_API_URL("revanced_alt_thumbnail_dearrow_api_url", STRING, "https://dearrow-thumb.ajay.app/api/v1/getThumbnail"), + ALT_THUMBNAIL_DEARROW_API_URL("revanced_alt_thumbnail_dearrow_api_url", STRING, + "https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true), CUSTOM_FILTER("revanced_custom_filter", BOOLEAN, FALSE), CUSTOM_FILTER_STRINGS("revanced_custom_filter_strings", STRING, "", true, parents(CUSTOM_FILTER)), DISABLE_FULLSCREEN_AMBIENT_MODE("revanced_disable_fullscreen_ambient_mode", BOOLEAN, TRUE, true), From a935fe3ba87e2c1700cbcbf315741b9188c665cd Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 5 Dec 2023 19:41:59 +0100 Subject: [PATCH 06/37] improve wording --- .../patches/AlternativeThumbnailsPatch.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index e9933a9d4c..23550c5cfd 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -37,7 +37,7 @@ */ public final class AlternativeThumbnailsPatch { @Nullable - private static final Uri dearrowApiUri; + private static final Uri dearrowApiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); static { // Fix any bad imported data. @@ -52,18 +52,16 @@ public final class AlternativeThumbnailsPatch { LogHelper.printException(() -> "Invalid alt thumbnail type: " + altThumbnailType); SettingsEnum.ALT_THUMBNAIL_TYPE.saveValue(SettingsEnum.ALT_THUMBNAIL_TYPE.defaultValue); } - - dearrowApiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); } /** - * Get the alternative thumbnail url using builtin beginning / middle / end thumbnails + * Build the alternative thumbnail url using video stills from the beginning / middle / end thumbnails. * * @param decodedUrl Decoded original thumbnail request url. * @return The alternative thumbnail url, or the original url. Both without tracking parameters. */ @NonNull - private static String getYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) { + private static String buildYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) { ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); if (qualityToUse == null) return decodedUrl.sanitizedUrl; // Video is a short. @@ -87,14 +85,14 @@ private static String getYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) { } /** - * Get the alternative thumbnail url using DeArrow thumbnail cache. + * Build the alternative thumbnail url using DeArrow thumbnail cache. * * @param videoId ID of the video to get a thumbnail of. * @param fallbackUrl URL to fall back to in case. * @return The alternative thumbnail url, without tracking parameters. */ @NonNull - private static String getDeArrowThumbnailURL(String videoId, String fallbackUrl) { + private static String buildDeArrowThumbnailURL(String videoId, String fallbackUrl) { // Use fallback if parsing API URI failed. if (dearrowApiUri == null) { return fallbackUrl; @@ -118,8 +116,8 @@ private static String getDeArrowThumbnailURL(String videoId, String fallbackUrl) */ public static String overrideImageURL(String originalUrl) { try { - final var mode = AlternativeThumbnailMode.getCurrent(); - if (mode == AlternativeThumbnailMode.ORIGINAL) { + final var thumbnailMode = AlternativeThumbnailMode.getCurrent(); + if (thumbnailMode == AlternativeThumbnailMode.ORIGINAL) { return originalUrl; } @@ -131,18 +129,19 @@ public static String overrideImageURL(String originalUrl) { // Keep any tracking parameters out of the logs, and log only the base URL. LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); - // Initialize thumbnail URL builder. - final StringBuilder thumbnailUrlBuilder; - if(mode.isDeArrow()) { - // figure out fallback url to use - final String fallbackUrl = mode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS - ? getYoutubeVideoStillURL(decodedUrl) : decodedUrl.sanitizedUrl; - final var deArrowUrl = getDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); - thumbnailUrlBuilder = new StringBuilder(deArrowUrl); + final StringBuilder thumbnailUrlBuilder = new StringBuilder(); + if (thumbnailMode.isDeArrow()) { + // Get fallback URL. + final String fallbackUrl = thumbnailMode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS + ? buildYoutubeVideoStillURL(decodedUrl) + : decodedUrl.sanitizedUrl; + + final var thumbnailURL = buildDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); + thumbnailUrlBuilder.append(thumbnailURL); } else { // Get video still URL. - final var videoStillUrl = getYoutubeVideoStillURL(decodedUrl); - thumbnailUrlBuilder = new StringBuilder(videoStillUrl); + final var thumbnailUrl = buildYoutubeVideoStillURL(decodedUrl); + thumbnailUrlBuilder.append(thumbnailUrl); // URL tracking parameters. Presumably they are to determine if a user has viewed a thumbnail. // This likely is used for recommendations, so they are retained if present. @@ -167,7 +166,8 @@ public static String overrideImageURL(String originalUrl) { public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { try { // 404 and alt thumbnails is using video stills - if (responseInfo.getHttpStatusCode() == 404 && AlternativeThumbnailMode.getCurrent() == AlternativeThumbnailMode.VIDEO_STILLS) { + if (responseInfo.getHttpStatusCode() == 404 && + AlternativeThumbnailMode.getCurrent() == AlternativeThumbnailMode.VIDEO_STILLS) { // Fast alt thumbnails is enabled and the thumbnail is not available. // The video is: // - live stream From 57f2bf9c749ee55c6c704a34b8a97cf5b8f23277 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:05:37 +0400 Subject: [PATCH 07/37] Handle if a thumbnail fails to load when using both fast still and DeArrow --- .../patches/AlternativeThumbnailsPatch.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 23550c5cfd..8966a6947e 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -130,7 +130,7 @@ public static String overrideImageURL(String originalUrl) { LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); final StringBuilder thumbnailUrlBuilder = new StringBuilder(); - if (thumbnailMode.isDeArrow()) { + if (thumbnailMode.usingDeArrow()) { // Get fallback URL. final String fallbackUrl = thumbnailMode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS ? buildYoutubeVideoStillURL(decodedUrl) @@ -166,8 +166,8 @@ public static String overrideImageURL(String originalUrl) { public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { try { // 404 and alt thumbnails is using video stills - if (responseInfo.getHttpStatusCode() == 404 && - AlternativeThumbnailMode.getCurrent() == AlternativeThumbnailMode.VIDEO_STILLS) { + if (responseInfo.getHttpStatusCode() == 404 + && AlternativeThumbnailMode.getCurrent().usingVideoStills()) { // Fast alt thumbnails is enabled and the thumbnail is not available. // The video is: // - live stream @@ -507,7 +507,11 @@ private enum AlternativeThumbnailMode { this.id = id; } - public boolean isDeArrow() { + public boolean usingVideoStills() { + return this == VIDEO_STILLS || this == DEARROW_OR_VIDEO_STILLS; + } + + public boolean usingDeArrow() { return this == DEARROW_OR_CREATOR_PROVIDED || this == DEARROW_OR_VIDEO_STILLS; } From 6f8c6f7f6e72bc232978880b9232f587669cf3f4 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 6 Dec 2023 01:46:33 +0400 Subject: [PATCH 08/37] - Handle DeArrow connection errors. - Fix validation of API url. --- .../patches/AlternativeThumbnailsPatch.java | 204 +++++++++++++----- .../integrations/settings/SettingsEnum.java | 11 +- .../org/chromium/net/UrlResponseInfo.java | 2 +- 3 files changed, 162 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 8966a6947e..6be8c7c7e3 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -1,14 +1,14 @@ package app.revanced.integrations.patches; import android.net.Uri; + import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import app.revanced.integrations.settings.SettingsEnum; -import app.revanced.integrations.utils.LogHelper; -import app.revanced.integrations.utils.ReVancedUtils; + import org.chromium.net.UrlResponseInfo; +import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashMap; @@ -16,11 +16,21 @@ import java.util.Map; import java.util.concurrent.ExecutionException; +import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.utils.LogHelper; +import app.revanced.integrations.utils.ReVancedUtils; + /** - * Alternative YouTube thumbnails, showing the beginning/middle/end of the video. + * Alternative YouTube thumbnails. + *

+ * Can show YouTube provided screen captures of beginning/middle/end of the video. * (ie: sd1.jpg, sd2.jpg, sd3.jpg). *

- * Has an additional option to use 'fast' thumbnails, + * Or can show crowd sourced thumbnails provided by DeArrow (http://dearrow.ajay.app). + *

+ * Or can use DeArrow and fall back to screen captures if DeArrow is not available. + *

+ * Has an additional option to use 'fast' video still thumbnails, * where it forces sd thumbnail quality and skips verifying if the alt thumbnail image exists. * The UI loading time will be the same or better than using the the original thumbnails, * but thumbnails will initially fail to load for all live streams, unreleased, and occasionally very old videos. @@ -36,22 +46,53 @@ * @noinspection unused */ public final class AlternativeThumbnailsPatch { - @Nullable - private static final Uri dearrowApiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); + + private static final Uri dearrowApiUri; + /** + * The scheme and host of {@link #dearrowApiUri}. + */ + private static final String deArrowApiUrlPrefix; + + /** + * How long to temporarily turn off DeArrow if it fails for any reason. + */ + private static final long DEARROW_CONNECTION_FAILURE_BACKOFF_MILLISECONDS = 2 * 60 * 1000; // 2 Minutes. + + /** + * If non zero, then the system time of when DeArrow API calls can resume. + */ + private static volatile long timeToResumeDeArrowAPICalls; static { - // Fix any bad imported data. + dearrowApiUri = validateSettings(); + deArrowApiUrlPrefix = dearrowApiUri.getScheme() + "://" + dearrowApiUri.getHost() + "/"; + } + + /** + * Fix any bad imported data. + */ + private static Uri validateSettings() { final int mode = SettingsEnum.ALT_THUMBNAIL_MODE.getInt(); - if(mode < 1 || mode > 3) { - LogHelper.printException(() -> "Invalid alt thumbnail mode: " + mode); - SettingsEnum.ALT_THUMBNAIL_MODE.saveValue(SettingsEnum.ALT_THUMBNAIL_MODE.defaultValue); + if (mode < 1 || mode > 4) { + ReVancedUtils.showToastLong("Invalid Alternative thumbnail mode: " + + mode + ". Using default"); + SettingsEnum.ALT_THUMBNAIL_MODE.resetToDefault(); } - final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_TYPE.getInt(); + final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_STILL_TYPE.getInt(); if (altThumbnailType < 1 || altThumbnailType > 3) { - LogHelper.printException(() -> "Invalid alt thumbnail type: " + altThumbnailType); - SettingsEnum.ALT_THUMBNAIL_TYPE.saveValue(SettingsEnum.ALT_THUMBNAIL_TYPE.defaultValue); + ReVancedUtils.showToastLong("Invalid Alternative still thumbnail type: " + + altThumbnailType + ". Using default"); + SettingsEnum.ALT_THUMBNAIL_STILL_TYPE.resetToDefault(); + } + + Uri apiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); + if (apiUri.getScheme() == null || apiUri.getHost() == null) { + ReVancedUtils.showToastLong("Invalid DeArrow API URL. Using default"); + SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.resetToDefault(); + return validateSettings(); } + return apiUri; } /** @@ -93,11 +134,6 @@ private static String buildYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) */ @NonNull private static String buildDeArrowThumbnailURL(String videoId, String fallbackUrl) { - // Use fallback if parsing API URI failed. - if (dearrowApiUri == null) { - return fallbackUrl; - } - // Build thumbnail request url. // See https://github.com/ajayyy/DeArrowThumbnailCache/blob/29eb4359ebdf823626c79d944a901492d760bbbc/app.py#L29. return dearrowApiUri @@ -109,6 +145,32 @@ private static String buildDeArrowThumbnailURL(String videoId, String fallbackUr .toString(); } + private static boolean urlIsDeArrow(@NonNull String imageUrl) { + return imageUrl.startsWith(deArrowApiUrlPrefix); + } + + /** + * @return If this client has not recently experience any DeArrow API errors. + */ + private static boolean canUseDeArrowAPI() { + if (timeToResumeDeArrowAPICalls == 0) { + return true; + } + if (System.currentTimeMillis() > timeToResumeDeArrowAPICalls) { + LogHelper.printDebug(() -> "Resuming DeArrow API calls"); + timeToResumeDeArrowAPICalls = 0; + return true; + } + return false; + } + + private static void handleDeArrowError(UrlResponseInfo responseInfo) { + // TODO? Add a setting to show a toast on DeArrow failure? + LogHelper.printDebug(() -> "Encountered DeArrow error. Backing off for " + + DEARROW_CONNECTION_FAILURE_BACKOFF_MILLISECONDS + "ms: " + responseInfo); + timeToResumeDeArrowAPICalls = System.currentTimeMillis() + DEARROW_CONNECTION_FAILURE_BACKOFF_MILLISECONDS; + } + /** * Injection point. Called off the main thread and by multiple threads at the same time. * @@ -130,7 +192,7 @@ public static String overrideImageURL(String originalUrl) { LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); final StringBuilder thumbnailUrlBuilder = new StringBuilder(); - if (thumbnailMode.usingDeArrow()) { + if (thumbnailMode.usingDeArrow() && canUseDeArrowAPI()) { // Get fallback URL. final String fallbackUrl = thumbnailMode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS ? buildYoutubeVideoStillURL(decodedUrl) @@ -138,7 +200,7 @@ public static String overrideImageURL(String originalUrl) { final var thumbnailURL = buildDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); thumbnailUrlBuilder.append(thumbnailURL); - } else { + } else if (thumbnailMode.usingVideoStills()) { // Get video still URL. final var thumbnailUrl = buildYoutubeVideoStillURL(decodedUrl); thumbnailUrlBuilder.append(thumbnailUrl); @@ -146,6 +208,8 @@ public static String overrideImageURL(String originalUrl) { // URL tracking parameters. Presumably they are to determine if a user has viewed a thumbnail. // This likely is used for recommendations, so they are retained if present. thumbnailUrlBuilder.append(decodedUrl.urlTrackingParameters); + } else { + return originalUrl; // Recently experienced DeArrow failure and video stills are not enabled. } final var thumbnailUrl = thumbnailUrlBuilder.toString(); @@ -165,29 +229,65 @@ public static String overrideImageURL(String originalUrl) { */ public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { try { - // 404 and alt thumbnails is using video stills - if (responseInfo.getHttpStatusCode() == 404 - && AlternativeThumbnailMode.getCurrent().usingVideoStills()) { - // Fast alt thumbnails is enabled and the thumbnail is not available. - // The video is: - // - live stream - // - upcoming unreleased video - // - very old - // - very low view count - // Take note of this, so if the image reloads the original thumbnail will be used. - DecodedThumbnailUrl decodedUrl = DecodedThumbnailUrl.decodeImageUrl(responseInfo.getUrl()); - if (decodedUrl == null) { - return; // Not a thumbnail. + final int responseCode = responseInfo.getHttpStatusCode(); + if (responseCode != 200) { + AlternativeThumbnailMode currentMode = AlternativeThumbnailMode.getCurrent(); + String url = responseInfo.getUrl(); + // Do not log the responseInfo unless it's known to be a DeArrow call. + // Otherwise this can log user details found in regular YouTube non Alt Thumbnails traffic. + LogHelper.printDebug(() -> "handleCronetSuccess responseCode: " + responseCode + " url: " + url); + + if (currentMode.usingDeArrow() && urlIsDeArrow(url)) { + handleDeArrowError(responseInfo); } - ThumbnailQuality quality = ThumbnailQuality.altImageNameToQuality(decodedUrl.imageQuality); - if (quality == null) { - // Video is a short or unknown quality, but the url returned 404. Should never happen. - LogHelper.printDebug(() -> "Failed to load unknown url: " + decodedUrl.sanitizedUrl); - return; + if (currentMode.usingVideoStills() && responseCode == 404) { + // Fast alt thumbnails is enabled and the thumbnail is not available. + // The video is: + // - live stream + // - upcoming unreleased video + // - very old + // - very low view count + // Take note of this, so if the image reloads the original thumbnail will be used. + DecodedThumbnailUrl decodedUrl = DecodedThumbnailUrl.decodeImageUrl(url); + if (decodedUrl == null) { + return; // Not a thumbnail. + } + + ThumbnailQuality quality = ThumbnailQuality.altImageNameToQuality(decodedUrl.imageQuality); + if (quality == null) { + // Video is a short or unknown quality, but the url returned 404. Should never happen. + LogHelper.printDebug(() -> "Failed to load unknown url: " + decodedUrl.sanitizedUrl); + return; + } + + VerifiedQualities.setAltThumbnailDoesNotExist(decodedUrl.videoId, quality); } + } + } catch (Exception ex) { + LogHelper.printException(() -> "Alt thumbnails callback failure", ex); + } + } - VerifiedQualities.setAltThumbnailDoesNotExist(decodedUrl.videoId, quality); + /** + * Injection point. + */ + public static void handleCronetFailure(@Nullable UrlResponseInfo responseInfo, IOException exception) { + try { + LogHelper.printDebug(() -> "handleCronetFailure exception: " + exception); + AlternativeThumbnailMode currentMode = AlternativeThumbnailMode.getCurrent(); + + if (currentMode.usingDeArrow()) { + // If the DeArrow API host name does not resolve, then no response is provided + // and the IOException (CronetException) provides no information to detect this situation. + // + // For now, treat this as a DeArrow failure but only if the API is not set to default. + // This may incorrectly turn off DeArrow for non alt thumbnail errors, + // but that should be rare since so few users will change the API url. + if ((responseInfo == null && !SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.isSetToDefault()) + || (responseInfo != null && urlIsDeArrow(responseInfo.getUrl()))) { + handleDeArrowError(responseInfo); + } } } catch (Exception ex) { LogHelper.printException(() -> "Alt thumbnails callback failure", ex); @@ -278,7 +378,7 @@ static ThumbnailQuality getQualityToUse(@NonNull String originalSize) { } String getAltImageNameToUse() { - return altImageName + SettingsEnum.ALT_THUMBNAIL_TYPE.getInt(); + return altImageName + SettingsEnum.ALT_THUMBNAIL_STILL_TYPE.getInt(); } } @@ -515,27 +615,25 @@ public boolean usingDeArrow() { return this == DEARROW_OR_CREATOR_PROVIDED || this == DEARROW_OR_VIDEO_STILLS; } - @Nullable + @NonNull public static AlternativeThumbnailMode byId(int id) { + // Could use the Enum ordinal and use values()[id], + // but then the ids would start at 0. + // Since only 4 ids exist this isn't a big deal + // and little overhead is needed to manually compare 4 int values. for (final var mode : AlternativeThumbnailMode.values()) { - if(mode.id == id) { + if (mode.id == id) { return mode; } } - - return null; + // User imported bad data and did not restart the app. Fix the settings and continue. + validateSettings(); + return byId(id); } @NonNull public static AlternativeThumbnailMode getCurrent() { - var mode = byId(SettingsEnum.ALT_THUMBNAIL_MODE.getInt()); - - if (mode == null) { - // Fallback to stock behaviour. - mode = ORIGINAL; - } - - return mode; + return byId(SettingsEnum.ALT_THUMBNAIL_MODE.getInt()); } } } diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index ef565d5e0f..29194fcb61 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -57,7 +57,8 @@ public enum SettingsEnum { // Layout ALT_THUMBNAIL_MODE("revanced_alt_thumbnail_mode", INTEGER, 1), - ALT_THUMBNAIL_TYPE("revanced_alt_thumbnail_type", INTEGER, 2), + @Deprecated DEPRECATED_ALT_THUMBNAIL_TYPE("revanced_alt_thumbnail_type", INTEGER, 2), + ALT_THUMBNAIL_STILL_TYPE("revanced_alt_thumbnail_still_type", INTEGER, 2), ALT_THUMBNAIL_FAST_QUALITY("revanced_alt_thumbnail_fast_quality", BOOLEAN, FALSE), ALT_THUMBNAIL_DEARROW_API_URL("revanced_alt_thumbnail_dearrow_api_url", STRING, "https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true), @@ -405,6 +406,7 @@ private static void loadAllSettings() { migrateOldSettingToNew(DISABLE_FINE_SCRUBBING_GESTURE, DISABLE_PRECISE_SEEKING_GESTURE); migrateOldSettingToNew(SHOW_OLD_VIDEO_QUALITY_MENU, RESTORE_OLD_VIDEO_QUALITY_MENU); migrateOldSettingToNew(ENABLE_OLD_SEEKBAR_THUMBNAILS, RESTORE_OLD_SEEKBAR_THUMBNAILS); + migrateOldSettingToNew(DEPRECATED_ALT_THUMBNAIL_TYPE, ALT_THUMBNAIL_STILL_TYPE); // Do _not_ delete this SB private user id migration property until sometime in 2024. // This is the only setting that cannot be reconfigured if lost, @@ -524,6 +526,13 @@ public void saveValue(@NonNull Object newValue) { } } + /** + * Identical to calling {@link #saveValue(Object)} using {@link #defaultValue}. + */ + public void resetToDefault() { + saveValue(defaultValue); + } + /** * @return if this setting can be configured and used. *

diff --git a/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java b/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java index 8e341247dc..31a75222b2 100644 --- a/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java +++ b/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java @@ -3,7 +3,7 @@ //dummy class public abstract class UrlResponseInfo { - public abstract String getUrl(); + public abstract String getUrl(); // Can return NULL for some failures. public abstract int getHttpStatusCode(); From be0301c1e2ab08c31cd02d9d40c40df50e70d93a Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 6 Dec 2023 02:02:35 +0400 Subject: [PATCH 09/37] comments, rename setting path --- .../patches/AlternativeThumbnailsPatch.java | 14 +++++++------- .../integrations/settings/SettingsEnum.java | 8 +++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 6be8c7c7e3..a118ce2ca0 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -79,11 +79,11 @@ private static Uri validateSettings() { SettingsEnum.ALT_THUMBNAIL_MODE.resetToDefault(); } - final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_STILL_TYPE.getInt(); + final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_STILL_TIME.getInt(); if (altThumbnailType < 1 || altThumbnailType > 3) { ReVancedUtils.showToastLong("Invalid Alternative still thumbnail type: " + altThumbnailType + ". Using default"); - SettingsEnum.ALT_THUMBNAIL_STILL_TYPE.resetToDefault(); + SettingsEnum.ALT_THUMBNAIL_STILL_TIME.resetToDefault(); } Uri apiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); @@ -343,7 +343,7 @@ static ThumbnailQuality getQualityToUse(@NonNull String originalSize) { return null; // Not a thumbnail for a regular video. } - final boolean useFastQuality = SettingsEnum.ALT_THUMBNAIL_FAST_QUALITY.getBoolean(); + final boolean useFastQuality = SettingsEnum.ALT_THUMBNAIL_STILL_FAST_QUALITY.getBoolean(); switch (quality) { case SDDEFAULT: // SD alt images have somewhat worse quality with washed out color and poor contrast. @@ -378,7 +378,7 @@ static ThumbnailQuality getQualityToUse(@NonNull String originalSize) { } String getAltImageNameToUse() { - return altImageName + SettingsEnum.ALT_THUMBNAIL_STILL_TYPE.getInt(); + return altImageName + SettingsEnum.ALT_THUMBNAIL_STILL_TIME.getInt(); } } @@ -423,7 +423,7 @@ private static VerifiedQualities getVerifiedQualities(@NonNull String videoId, b static boolean verifyAltThumbnailExist(@NonNull String videoId, @NonNull ThumbnailQuality quality, @NonNull String imageUrl) { - VerifiedQualities verified = getVerifiedQualities(videoId, SettingsEnum.ALT_THUMBNAIL_FAST_QUALITY.getBoolean()); + VerifiedQualities verified = getVerifiedQualities(videoId, SettingsEnum.ALT_THUMBNAIL_STILL_FAST_QUALITY.getBoolean()); if (verified == null) return true; // Fast alt thumbnails is enabled. return verified.verifyYouTubeThumbnailExists(videoId, quality, imageUrl); } @@ -474,7 +474,7 @@ synchronized boolean verifyYouTubeThumbnailExists(@NonNull String videoId, @NonN return true; // Previously verified as existing. } - final boolean fastQuality = SettingsEnum.ALT_THUMBNAIL_FAST_QUALITY.getBoolean(); + final boolean fastQuality = SettingsEnum.ALT_THUMBNAIL_STILL_FAST_QUALITY.getBoolean(); if (lowestQualityNotAvailable != null && lowestQualityNotAvailable.ordinal() <= quality.ordinal()) { if (fastQuality || System.currentTimeMillis() < timeToReVerifyLowestQuality) { return false; // Previously verified as not existing. @@ -588,7 +588,7 @@ private enum AlternativeThumbnailMode { /** * Use video stills provided by YouTube. - * revanced_alt_thumbnail_type and revanced_alt_thumbnail_fast_quality controls what still should be used. + * Uses {@link SettingsEnum#ALT_THUMBNAIL_STILL_TIME} and {@link SettingsEnum#ALT_THUMBNAIL_STILL_FAST_QUALITY}. */ VIDEO_STILLS(2), diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index 29194fcb61..ae50d1e779 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -58,8 +58,9 @@ public enum SettingsEnum { // Layout ALT_THUMBNAIL_MODE("revanced_alt_thumbnail_mode", INTEGER, 1), @Deprecated DEPRECATED_ALT_THUMBNAIL_TYPE("revanced_alt_thumbnail_type", INTEGER, 2), - ALT_THUMBNAIL_STILL_TYPE("revanced_alt_thumbnail_still_type", INTEGER, 2), - ALT_THUMBNAIL_FAST_QUALITY("revanced_alt_thumbnail_fast_quality", BOOLEAN, FALSE), + ALT_THUMBNAIL_STILL_TIME("revanced_alt_thumbnail_still_time", INTEGER, 2), + @Deprecated DEPRECATED_ALT_THUMBNAIL_FAST_QUALITY("revanced_alt_thumbnail_fast_quality", BOOLEAN, FALSE), + ALT_THUMBNAIL_STILL_FAST_QUALITY("revanced_alt_thumbnail_still_fast_quality", BOOLEAN, FALSE), ALT_THUMBNAIL_DEARROW_API_URL("revanced_alt_thumbnail_dearrow_api_url", STRING, "https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true), CUSTOM_FILTER("revanced_custom_filter", BOOLEAN, FALSE), @@ -406,7 +407,8 @@ private static void loadAllSettings() { migrateOldSettingToNew(DISABLE_FINE_SCRUBBING_GESTURE, DISABLE_PRECISE_SEEKING_GESTURE); migrateOldSettingToNew(SHOW_OLD_VIDEO_QUALITY_MENU, RESTORE_OLD_VIDEO_QUALITY_MENU); migrateOldSettingToNew(ENABLE_OLD_SEEKBAR_THUMBNAILS, RESTORE_OLD_SEEKBAR_THUMBNAILS); - migrateOldSettingToNew(DEPRECATED_ALT_THUMBNAIL_TYPE, ALT_THUMBNAIL_STILL_TYPE); + migrateOldSettingToNew(DEPRECATED_ALT_THUMBNAIL_TYPE, ALT_THUMBNAIL_STILL_TIME); + migrateOldSettingToNew(DEPRECATED_ALT_THUMBNAIL_FAST_QUALITY, ALT_THUMBNAIL_STILL_FAST_QUALITY); // Do _not_ delete this SB private user id migration property until sometime in 2024. // This is the only setting that cannot be reconfigured if lost, From 67a806793777aba41b13f8ad1fc7e6a2fe073353 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 6 Dec 2023 02:16:19 +0400 Subject: [PATCH 10/37] adjust logging --- .../patches/AlternativeThumbnailsPatch.java | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index a118ce2ca0..04e4336ddb 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -192,32 +192,38 @@ public static String overrideImageURL(String originalUrl) { LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); final StringBuilder thumbnailUrlBuilder = new StringBuilder(); + final String sanitizedReplacementUrl; + final boolean includeTrackingParameters; if (thumbnailMode.usingDeArrow() && canUseDeArrowAPI()) { // Get fallback URL. + // Do not include view tracking parameters with API call. final String fallbackUrl = thumbnailMode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS ? buildYoutubeVideoStillURL(decodedUrl) : decodedUrl.sanitizedUrl; - final var thumbnailURL = buildDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); - thumbnailUrlBuilder.append(thumbnailURL); + sanitizedReplacementUrl = buildDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); + includeTrackingParameters = false; } else if (thumbnailMode.usingVideoStills()) { // Get video still URL. - final var thumbnailUrl = buildYoutubeVideoStillURL(decodedUrl); - thumbnailUrlBuilder.append(thumbnailUrl); - - // URL tracking parameters. Presumably they are to determine if a user has viewed a thumbnail. - // This likely is used for recommendations, so they are retained if present. - thumbnailUrlBuilder.append(decodedUrl.urlTrackingParameters); + sanitizedReplacementUrl = buildYoutubeVideoStillURL(decodedUrl); + includeTrackingParameters = true; } else { return originalUrl; // Recently experienced DeArrow failure and video stills are not enabled. } - final var thumbnailUrl = thumbnailUrlBuilder.toString(); - LogHelper.printDebug(() -> "Replaced url: " + thumbnailUrl); + thumbnailUrlBuilder.append(sanitizedReplacementUrl); + if (includeTrackingParameters) { + // View tracking parameters are presumably used to determine if a user has viewed a thumbnail. + // This likely is used for recommendations, so they are retained if present. + thumbnailUrlBuilder.append(decodedUrl.viewTrackingParameters); + } + + // Do not log the tracking parameters. + LogHelper.printDebug(() -> "Replacement url: " + sanitizedReplacementUrl); - return thumbnailUrl; + return sanitizedReplacementUrl; } catch (Exception ex) { - LogHelper.printException(() -> "Alt thumbnails failure", ex); + LogHelper.printException(() -> "overrideImageURL failure", ex); return originalUrl; } } @@ -233,7 +239,7 @@ public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { if (responseCode != 200) { AlternativeThumbnailMode currentMode = AlternativeThumbnailMode.getCurrent(); String url = responseInfo.getUrl(); - // Do not log the responseInfo unless it's known to be a DeArrow call. + // Do not log the responseInfo unless it's found to be a DeArrow call. // Otherwise this can log user details found in regular YouTube non Alt Thumbnails traffic. LogHelper.printDebug(() -> "handleCronetSuccess responseCode: " + responseCode + " url: " + url); @@ -265,7 +271,7 @@ public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { } } } catch (Exception ex) { - LogHelper.printException(() -> "Alt thumbnails callback failure", ex); + LogHelper.printException(() -> "Callback success error", ex); } } @@ -290,7 +296,7 @@ public static void handleCronetFailure(@Nullable UrlResponseInfo responseInfo, I } } } catch (Exception ex) { - LogHelper.printException(() -> "Alt thumbnails callback failure", ex); + LogHelper.printException(() -> "Callback failure error", ex); } } @@ -562,7 +568,7 @@ static DecodedThumbnailUrl decodeImageUrl(String url) { /** JPG or WEBP */ final String imageExtension; /** User view tracking parameters, only present on some images. */ - final String urlTrackingParameters; + final String viewTrackingParameters; private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEndIndex, int imageSizeStartIndex, int imageSizeEndIndex, int imageExtensionEndIndex) { @@ -571,7 +577,7 @@ private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEn videoId = fullUrl.substring(videoIdStartIndex, videoIdEndIndex); imageQuality = fullUrl.substring(imageSizeStartIndex, imageSizeEndIndex); imageExtension = fullUrl.substring(imageSizeEndIndex + 1, imageExtensionEndIndex); - urlTrackingParameters = (imageExtensionEndIndex == fullUrl.length()) + viewTrackingParameters = (imageExtensionEndIndex == fullUrl.length()) ? "" : fullUrl.substring(imageExtensionEndIndex); } } From 19559f99b90d762bcd21945a30e2aab938a9e985 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 6 Dec 2023 02:26:43 +0400 Subject: [PATCH 11/37] refactor --- .../patches/AlternativeThumbnailsPatch.java | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 04e4336ddb..af6792249b 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -106,23 +106,11 @@ private static String buildYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); if (qualityToUse == null) return decodedUrl.sanitizedUrl; // Video is a short. - // Images could be upgraded to webp if they are not already, but this fails quite often, - // especially for new videos uploaded in the last hour. - // And even if alt webp images do exist, sometimes they can load much slower than the original jpg alt images. - // (as much as 4x slower has been observed, despite the alt webp image being a smaller file). - - StringBuilder builder = new StringBuilder(decodedUrl.sanitizedUrl.length() + 2); - builder.append(decodedUrl.urlPrefix); - builder.append(decodedUrl.videoId).append('/'); - builder.append(qualityToUse.getAltImageNameToUse()); - builder.append('.').append(decodedUrl.imageExtension); - - String sanitizedReplacement = builder.toString(); - if (!VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { - return decodedUrl.sanitizedUrl; + String sanitizedReplacement = decodedUrl.createStillUrlWithQuality(qualityToUse, false); + if (VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { + return sanitizedReplacement; } - - return builder.toString(); + return decodedUrl.sanitizedUrl; } /** @@ -558,6 +546,7 @@ static DecodedThumbnailUrl decodeImageUrl(String url) { imageSizeStartIndex, imageSizeEndIndex, imageExtensionEndIndex); } + final String originalFullUrl; /** Full usable url, but stripped of any tracking information. */ final String sanitizedUrl; /** Url up to the video ID. */ @@ -572,6 +561,7 @@ static DecodedThumbnailUrl decodeImageUrl(String url) { private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEndIndex, int imageSizeStartIndex, int imageSizeEndIndex, int imageExtensionEndIndex) { + originalFullUrl = fullUrl; sanitizedUrl = fullUrl.substring(0, imageExtensionEndIndex); urlPrefix = fullUrl.substring(0, videoIdStartIndex); videoId = fullUrl.substring(videoIdStartIndex, videoIdEndIndex); @@ -580,6 +570,22 @@ private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEn viewTrackingParameters = (imageExtensionEndIndex == fullUrl.length()) ? "" : fullUrl.substring(imageExtensionEndIndex); } + + String createStillUrlWithQuality(@NonNull ThumbnailQuality qualityToUse, boolean includeViewTracking) { + // Images could be upgraded to webp if they are not already, but this fails quite often, + // especially for new videos uploaded in the last hour. + // And even if alt webp images do exist, sometimes they can load much slower than the original jpg alt images. + // (as much as 4x slower has been observed, despite the alt webp image being a smaller file). + StringBuilder builder = new StringBuilder(originalFullUrl.length() + 2); + builder.append(urlPrefix); + builder.append(videoId).append('/'); + builder.append(qualityToUse.getAltImageNameToUse()); + builder.append('.').append(imageExtension); + if (includeViewTracking) { + builder.append(viewTrackingParameters); + } + return builder.toString(); + } } /** From bd51f08deb825be3ebff1c9549ef26c57cd36a51 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 6 Dec 2023 02:58:01 +0400 Subject: [PATCH 12/37] Use background thread to send dummy calls for view tracking. This may not work correctly because YouTube includes many device and user parameters in the request headers. Committing this code for any future reference. --- .../patches/AlternativeThumbnailsPatch.java | 71 +++++++++++++++++-- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index af6792249b..41b61b709e 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -15,6 +15,8 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import app.revanced.integrations.settings.SettingsEnum; import app.revanced.integrations.utils.LogHelper; @@ -47,6 +49,32 @@ */ public final class AlternativeThumbnailsPatch { + public static final int CONNECTION_TIMEOUT_MILLIS = 5000; + + /** + * Single thread executor with the lowest priority. + * Used simulate loading the original thumbnail with the view tracking parameters. + * Only used when DeArrow is enabled as the tracking parameters are stripped from it's + * redirect url parameter. + */ + private static final ExecutorService viewTrackingExecutor = Executors.newSingleThreadExecutor(r -> { + Thread thread = new Thread(r); + thread.setPriority(Thread.MIN_PRIORITY); + return thread; + }); + + /** + * Used to prevent sending view tracking parameters more than once. + */ + @GuardedBy("itself") + private static final Map videoIdsTrackingSent = new LinkedHashMap<>(100) { + private static final int CACHE_LIMIT = 1000; + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > CACHE_LIMIT; // Evict the oldest entry if over the cache limit. + } + }; + private static final Uri dearrowApiUri; /** * The scheme and host of {@link #dearrowApiUri}. @@ -106,7 +134,7 @@ private static String buildYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); if (qualityToUse == null) return decodedUrl.sanitizedUrl; // Video is a short. - String sanitizedReplacement = decodedUrl.createStillUrlWithQuality(qualityToUse, false); + String sanitizedReplacement = decodedUrl.createStillUrl(qualityToUse.getAltImageNameToUse(), false); if (VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { return sanitizedReplacement; } @@ -159,6 +187,35 @@ private static void handleDeArrowError(UrlResponseInfo responseInfo) { timeToResumeDeArrowAPICalls = System.currentTimeMillis() + DEARROW_CONNECTION_FAILURE_BACKOFF_MILLISECONDS; } + /** + * Because the view tracking parameters are not included and likely are used for recommendations, + * make a dummy call in the background for the original thumbnail and include the tracking parameters. + * + * This may do nothing if YouTube does not detect the + */ + private static void makeDummyViewTrackingCall(@NonNull DecodedThumbnailUrl decodedUrl) { + if (decodedUrl.viewTrackingParameters.isEmpty()) { + return; // Nothing to do. + } + if (videoIdsTrackingSent.put(decodedUrl.videoId, Boolean.TRUE) != null) { + return; // Already sent tracking for this video. + } + viewTrackingExecutor.execute(() -> { + try { + String qualityToUse = ThumbnailQuality.DEFAULT.originalName; // Use the lowest quality. + String lowQualityWithTracking = decodedUrl.createStillUrl(qualityToUse, true); + HttpURLConnection connection = (HttpURLConnection) new URL(lowQualityWithTracking).openConnection(); + connection.setConnectTimeout(CONNECTION_TIMEOUT_MILLIS); + connection.setReadTimeout(CONNECTION_TIMEOUT_MILLIS); + final int responseCode = connection.getResponseCode(); + LogHelper.printDebug(() -> "Finished sending viewing parameters for video: " + + decodedUrl.videoId + " with response: " + responseCode); + } catch (Exception ex) { + LogHelper.printInfo(() -> "View tracking failure", ex); + } + }); + } + /** * Injection point. Called off the main thread and by multiple threads at the same time. * @@ -184,11 +241,12 @@ public static String overrideImageURL(String originalUrl) { final boolean includeTrackingParameters; if (thumbnailMode.usingDeArrow() && canUseDeArrowAPI()) { // Get fallback URL. - // Do not include view tracking parameters with API call. final String fallbackUrl = thumbnailMode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS ? buildYoutubeVideoStillURL(decodedUrl) : decodedUrl.sanitizedUrl; + makeDummyViewTrackingCall(decodedUrl); + sanitizedReplacementUrl = buildDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); includeTrackingParameters = false; } else if (thumbnailMode.usingVideoStills()) { @@ -489,10 +547,9 @@ synchronized boolean verifyYouTubeThumbnailExists(@NonNull String videoId, @NonN // to run the url connection thru the integrations thread pool which runs at the highest priority. final long start = System.currentTimeMillis(); imageFileFound = ReVancedUtils.submitOnBackgroundThread(() -> { - final int connectionTimeoutMillis = 5000; HttpURLConnection connection = (HttpURLConnection) new URL(imageUrl).openConnection(); - connection.setConnectTimeout(connectionTimeoutMillis); - connection.setReadTimeout(connectionTimeoutMillis); + connection.setConnectTimeout(CONNECTION_TIMEOUT_MILLIS); + connection.setReadTimeout(CONNECTION_TIMEOUT_MILLIS); connection.setRequestMethod("HEAD"); // Even with a HEAD request, the response is the same size as a full GET request. // Using an empty range fixes this. @@ -571,7 +628,7 @@ private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEn ? "" : fullUrl.substring(imageExtensionEndIndex); } - String createStillUrlWithQuality(@NonNull ThumbnailQuality qualityToUse, boolean includeViewTracking) { + String createStillUrl(@NonNull String imageQualityName, boolean includeViewTracking) { // Images could be upgraded to webp if they are not already, but this fails quite often, // especially for new videos uploaded in the last hour. // And even if alt webp images do exist, sometimes they can load much slower than the original jpg alt images. @@ -579,7 +636,7 @@ String createStillUrlWithQuality(@NonNull ThumbnailQuality qualityToUse, boolean StringBuilder builder = new StringBuilder(originalFullUrl.length() + 2); builder.append(urlPrefix); builder.append(videoId).append('/'); - builder.append(qualityToUse.getAltImageNameToUse()); + builder.append(imageQualityName); builder.append('.').append(imageExtension); if (includeViewTracking) { builder.append(viewTrackingParameters); From 0b404c63c2c07f5f1bcb02c9d3027cae5e3200f0 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 6 Dec 2023 02:59:12 +0400 Subject: [PATCH 13/37] Revert "Use background thread to send dummy calls for view tracking. This may not work correctly because YouTube includes many device and user parameters in the request headers. Committing this code for any future reference." This reverts commit bd51f08deb825be3ebff1c9549ef26c57cd36a51. --- .../patches/AlternativeThumbnailsPatch.java | 71 ++----------------- 1 file changed, 7 insertions(+), 64 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 41b61b709e..af6792249b 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -15,8 +15,6 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import app.revanced.integrations.settings.SettingsEnum; import app.revanced.integrations.utils.LogHelper; @@ -49,32 +47,6 @@ */ public final class AlternativeThumbnailsPatch { - public static final int CONNECTION_TIMEOUT_MILLIS = 5000; - - /** - * Single thread executor with the lowest priority. - * Used simulate loading the original thumbnail with the view tracking parameters. - * Only used when DeArrow is enabled as the tracking parameters are stripped from it's - * redirect url parameter. - */ - private static final ExecutorService viewTrackingExecutor = Executors.newSingleThreadExecutor(r -> { - Thread thread = new Thread(r); - thread.setPriority(Thread.MIN_PRIORITY); - return thread; - }); - - /** - * Used to prevent sending view tracking parameters more than once. - */ - @GuardedBy("itself") - private static final Map videoIdsTrackingSent = new LinkedHashMap<>(100) { - private static final int CACHE_LIMIT = 1000; - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > CACHE_LIMIT; // Evict the oldest entry if over the cache limit. - } - }; - private static final Uri dearrowApiUri; /** * The scheme and host of {@link #dearrowApiUri}. @@ -134,7 +106,7 @@ private static String buildYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); if (qualityToUse == null) return decodedUrl.sanitizedUrl; // Video is a short. - String sanitizedReplacement = decodedUrl.createStillUrl(qualityToUse.getAltImageNameToUse(), false); + String sanitizedReplacement = decodedUrl.createStillUrlWithQuality(qualityToUse, false); if (VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { return sanitizedReplacement; } @@ -187,35 +159,6 @@ private static void handleDeArrowError(UrlResponseInfo responseInfo) { timeToResumeDeArrowAPICalls = System.currentTimeMillis() + DEARROW_CONNECTION_FAILURE_BACKOFF_MILLISECONDS; } - /** - * Because the view tracking parameters are not included and likely are used for recommendations, - * make a dummy call in the background for the original thumbnail and include the tracking parameters. - * - * This may do nothing if YouTube does not detect the - */ - private static void makeDummyViewTrackingCall(@NonNull DecodedThumbnailUrl decodedUrl) { - if (decodedUrl.viewTrackingParameters.isEmpty()) { - return; // Nothing to do. - } - if (videoIdsTrackingSent.put(decodedUrl.videoId, Boolean.TRUE) != null) { - return; // Already sent tracking for this video. - } - viewTrackingExecutor.execute(() -> { - try { - String qualityToUse = ThumbnailQuality.DEFAULT.originalName; // Use the lowest quality. - String lowQualityWithTracking = decodedUrl.createStillUrl(qualityToUse, true); - HttpURLConnection connection = (HttpURLConnection) new URL(lowQualityWithTracking).openConnection(); - connection.setConnectTimeout(CONNECTION_TIMEOUT_MILLIS); - connection.setReadTimeout(CONNECTION_TIMEOUT_MILLIS); - final int responseCode = connection.getResponseCode(); - LogHelper.printDebug(() -> "Finished sending viewing parameters for video: " - + decodedUrl.videoId + " with response: " + responseCode); - } catch (Exception ex) { - LogHelper.printInfo(() -> "View tracking failure", ex); - } - }); - } - /** * Injection point. Called off the main thread and by multiple threads at the same time. * @@ -241,12 +184,11 @@ public static String overrideImageURL(String originalUrl) { final boolean includeTrackingParameters; if (thumbnailMode.usingDeArrow() && canUseDeArrowAPI()) { // Get fallback URL. + // Do not include view tracking parameters with API call. final String fallbackUrl = thumbnailMode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS ? buildYoutubeVideoStillURL(decodedUrl) : decodedUrl.sanitizedUrl; - makeDummyViewTrackingCall(decodedUrl); - sanitizedReplacementUrl = buildDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); includeTrackingParameters = false; } else if (thumbnailMode.usingVideoStills()) { @@ -547,9 +489,10 @@ synchronized boolean verifyYouTubeThumbnailExists(@NonNull String videoId, @NonN // to run the url connection thru the integrations thread pool which runs at the highest priority. final long start = System.currentTimeMillis(); imageFileFound = ReVancedUtils.submitOnBackgroundThread(() -> { + final int connectionTimeoutMillis = 5000; HttpURLConnection connection = (HttpURLConnection) new URL(imageUrl).openConnection(); - connection.setConnectTimeout(CONNECTION_TIMEOUT_MILLIS); - connection.setReadTimeout(CONNECTION_TIMEOUT_MILLIS); + connection.setConnectTimeout(connectionTimeoutMillis); + connection.setReadTimeout(connectionTimeoutMillis); connection.setRequestMethod("HEAD"); // Even with a HEAD request, the response is the same size as a full GET request. // Using an empty range fixes this. @@ -628,7 +571,7 @@ private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEn ? "" : fullUrl.substring(imageExtensionEndIndex); } - String createStillUrl(@NonNull String imageQualityName, boolean includeViewTracking) { + String createStillUrlWithQuality(@NonNull ThumbnailQuality qualityToUse, boolean includeViewTracking) { // Images could be upgraded to webp if they are not already, but this fails quite often, // especially for new videos uploaded in the last hour. // And even if alt webp images do exist, sometimes they can load much slower than the original jpg alt images. @@ -636,7 +579,7 @@ String createStillUrl(@NonNull String imageQualityName, boolean includeViewTrack StringBuilder builder = new StringBuilder(originalFullUrl.length() + 2); builder.append(urlPrefix); builder.append(videoId).append('/'); - builder.append(imageQualityName); + builder.append(qualityToUse.getAltImageNameToUse()); builder.append('.').append(imageExtension); if (includeViewTracking) { builder.append(viewTrackingParameters); From efe12e9eb4d2bc8b1209959a9eee9d7d80824ce5 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 6 Dec 2023 03:04:31 +0400 Subject: [PATCH 14/37] refactor --- .../integrations/patches/AlternativeThumbnailsPatch.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index af6792249b..a78df12197 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -106,7 +106,7 @@ private static String buildYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); if (qualityToUse == null) return decodedUrl.sanitizedUrl; // Video is a short. - String sanitizedReplacement = decodedUrl.createStillUrlWithQuality(qualityToUse, false); + String sanitizedReplacement = decodedUrl.createStillUrl(qualityToUse, false); if (VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { return sanitizedReplacement; } @@ -571,7 +571,7 @@ private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEn ? "" : fullUrl.substring(imageExtensionEndIndex); } - String createStillUrlWithQuality(@NonNull ThumbnailQuality qualityToUse, boolean includeViewTracking) { + String createStillUrl(@NonNull ThumbnailQuality qualityToUse, boolean includeViewTracking) { // Images could be upgraded to webp if they are not already, but this fails quite often, // especially for new videos uploaded in the last hour. // And even if alt webp images do exist, sometimes they can load much slower than the original jpg alt images. From b1fd216227d0c6e4f1b453e1174cdf0d8c97842f Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 6 Dec 2023 12:38:20 +0400 Subject: [PATCH 15/37] don't back off API if non DeArrow error happens --- .../patches/AlternativeThumbnailsPatch.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index a78df12197..a5f87daad4 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -274,12 +274,10 @@ public static void handleCronetFailure(@Nullable UrlResponseInfo responseInfo, I if (currentMode.usingDeArrow()) { // If the DeArrow API host name does not resolve, then no response is provided // and the IOException (CronetException) provides no information to detect this situation. - // - // For now, treat this as a DeArrow failure but only if the API is not set to default. - // This may incorrectly turn off DeArrow for non alt thumbnail errors, - // but that should be rare since so few users will change the API url. - if ((responseInfo == null && !SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.isSetToDefault()) - || (responseInfo != null && urlIsDeArrow(responseInfo.getUrl()))) { + // For this situation no error toast is shown and no API backoff is done, + // since this situation cannot be easily detected. Will not happen + // unless the user has changed the API url. + if (responseInfo != null && urlIsDeArrow(responseInfo.getUrl())) { handleDeArrowError(responseInfo); } } From 8881daa0000d2c8755df6d2f839b173b260fa643 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 6 Dec 2023 12:43:45 +0400 Subject: [PATCH 16/37] fix logic, cleanup --- .../patches/AlternativeThumbnailsPatch.java | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index a5f87daad4..51e62f92a4 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -179,9 +179,7 @@ public static String overrideImageURL(String originalUrl) { // Keep any tracking parameters out of the logs, and log only the base URL. LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); - final StringBuilder thumbnailUrlBuilder = new StringBuilder(); - final String sanitizedReplacementUrl; - final boolean includeTrackingParameters; + String sanitizedReplacementUrl; if (thumbnailMode.usingDeArrow() && canUseDeArrowAPI()) { // Get fallback URL. // Do not include view tracking parameters with API call. @@ -190,22 +188,14 @@ public static String overrideImageURL(String originalUrl) { : decodedUrl.sanitizedUrl; sanitizedReplacementUrl = buildDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); - includeTrackingParameters = false; } else if (thumbnailMode.usingVideoStills()) { // Get video still URL. - sanitizedReplacementUrl = buildYoutubeVideoStillURL(decodedUrl); - includeTrackingParameters = true; + // Include view tracking parameters if present. + sanitizedReplacementUrl = buildYoutubeVideoStillURL(decodedUrl) + decodedUrl.viewTrackingParameters; } else { return originalUrl; // Recently experienced DeArrow failure and video stills are not enabled. } - thumbnailUrlBuilder.append(sanitizedReplacementUrl); - if (includeTrackingParameters) { - // View tracking parameters are presumably used to determine if a user has viewed a thumbnail. - // This likely is used for recommendations, so they are retained if present. - thumbnailUrlBuilder.append(decodedUrl.viewTrackingParameters); - } - // Do not log the tracking parameters. LogHelper.printDebug(() -> "Replacement url: " + sanitizedReplacementUrl); @@ -227,9 +217,6 @@ public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { if (responseCode != 200) { AlternativeThumbnailMode currentMode = AlternativeThumbnailMode.getCurrent(); String url = responseInfo.getUrl(); - // Do not log the responseInfo unless it's found to be a DeArrow call. - // Otherwise this can log user details found in regular YouTube non Alt Thumbnails traffic. - LogHelper.printDebug(() -> "handleCronetSuccess responseCode: " + responseCode + " url: " + url); if (currentMode.usingDeArrow() && urlIsDeArrow(url)) { handleDeArrowError(responseInfo); @@ -248,6 +235,8 @@ public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { return; // Not a thumbnail. } + LogHelper.printDebug(() -> "handleCronetSuccess responseCode: " + responseCode + " url: " + url); + ThumbnailQuality quality = ThumbnailQuality.altImageNameToQuality(decodedUrl.imageQuality); if (quality == null) { // Video is a short or unknown quality, but the url returned 404. Should never happen. @@ -272,7 +261,7 @@ public static void handleCronetFailure(@Nullable UrlResponseInfo responseInfo, I AlternativeThumbnailMode currentMode = AlternativeThumbnailMode.getCurrent(); if (currentMode.usingDeArrow()) { - // If the DeArrow API host name does not resolve, then no response is provided + // If the DeArrow API host name does not resolve, then the response is nbull // and the IOException (CronetException) provides no information to detect this situation. // For this situation no error toast is shown and no API backoff is done, // since this situation cannot be easily detected. Will not happen @@ -626,7 +615,8 @@ public boolean usingDeArrow() { } @NonNull - public static AlternativeThumbnailMode byId(int id) { + public static AlternativeThumbnailMode getCurrent() { + final var id = SettingsEnum.ALT_THUMBNAIL_MODE.getInt(); // Could use the Enum ordinal and use values()[id], // but then the ids would start at 0. // Since only 4 ids exist this isn't a big deal @@ -638,12 +628,8 @@ public static AlternativeThumbnailMode byId(int id) { } // User imported bad data and did not restart the app. Fix the settings and continue. validateSettings(); - return byId(id); + return getCurrent(); } - @NonNull - public static AlternativeThumbnailMode getCurrent() { - return byId(SettingsEnum.ALT_THUMBNAIL_MODE.getInt()); - } } } From 224fda7d48751f36891158d513239c82e12d44e2 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:37:42 +0400 Subject: [PATCH 17/37] Use dynamically updated about summary --- .../patches/AlternativeThumbnailsPatch.java | 112 +++++------------- .../integrations/settings/SettingsEnum.java | 13 +- .../AlternativeThumbnailsAboutPreference.java | 96 +++++++++++++++ 3 files changed, 128 insertions(+), 93 deletions(-) create mode 100644 app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutPreference.java diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 51e62f92a4..cc574283ab 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -72,18 +72,11 @@ public final class AlternativeThumbnailsPatch { * Fix any bad imported data. */ private static Uri validateSettings() { - final int mode = SettingsEnum.ALT_THUMBNAIL_MODE.getInt(); - if (mode < 1 || mode > 4) { - ReVancedUtils.showToastLong("Invalid Alternative thumbnail mode: " - + mode + ". Using default"); - SettingsEnum.ALT_THUMBNAIL_MODE.resetToDefault(); - } - - final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_STILL_TIME.getInt(); + final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_STILLS_TIME.getInt(); if (altThumbnailType < 1 || altThumbnailType > 3) { ReVancedUtils.showToastLong("Invalid Alternative still thumbnail type: " + altThumbnailType + ". Using default"); - SettingsEnum.ALT_THUMBNAIL_STILL_TIME.resetToDefault(); + SettingsEnum.ALT_THUMBNAIL_STILLS_TIME.resetToDefault(); } Uri apiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); @@ -95,8 +88,16 @@ private static Uri validateSettings() { return apiUri; } + private static boolean usingDeArrow() { + return SettingsEnum.ALT_THUMBNAIL_DEARROW.getBoolean(); + } + + private static boolean usingVideoStills() { + return SettingsEnum.ALT_THUMBNAIL_STILLS.getBoolean(); + } + /** - * Build the alternative thumbnail url using video stills from the beginning / middle / end thumbnails. + * Build the alternative thumbnail url using YouTube provided still video captures. * * @param decodedUrl Decoded original thumbnail request url. * @return The alternative thumbnail url, or the original url. Both without tracking parameters. @@ -106,7 +107,7 @@ private static String buildYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); if (qualityToUse == null) return decodedUrl.sanitizedUrl; // Video is a short. - String sanitizedReplacement = decodedUrl.createStillUrl(qualityToUse, false); + String sanitizedReplacement = decodedUrl.createStillsUrl(qualityToUse, false); if (VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { return sanitizedReplacement; } @@ -116,7 +117,7 @@ private static String buildYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) /** * Build the alternative thumbnail url using DeArrow thumbnail cache. * - * @param videoId ID of the video to get a thumbnail of. + * @param videoId ID of the video to get a thumbnail of. Can be any video (regular or Short). * @param fallbackUrl URL to fall back to in case. * @return The alternative thumbnail url, without tracking parameters. */ @@ -166,8 +167,9 @@ private static void handleDeArrowError(UrlResponseInfo responseInfo) { */ public static String overrideImageURL(String originalUrl) { try { - final var thumbnailMode = AlternativeThumbnailMode.getCurrent(); - if (thumbnailMode == AlternativeThumbnailMode.ORIGINAL) { + final boolean usingVideoStills = usingVideoStills(); + final boolean usingDeArrow = usingDeArrow(); + if (!usingVideoStills && !usingDeArrow) { return originalUrl; } @@ -180,15 +182,15 @@ public static String overrideImageURL(String originalUrl) { LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); String sanitizedReplacementUrl; - if (thumbnailMode.usingDeArrow() && canUseDeArrowAPI()) { + if (usingDeArrow && canUseDeArrowAPI()) { // Get fallback URL. // Do not include view tracking parameters with API call. - final String fallbackUrl = thumbnailMode == AlternativeThumbnailMode.DEARROW_OR_VIDEO_STILLS + final String fallbackUrl = usingVideoStills ? buildYoutubeVideoStillURL(decodedUrl) : decodedUrl.sanitizedUrl; sanitizedReplacementUrl = buildDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); - } else if (thumbnailMode.usingVideoStills()) { + } else if (usingVideoStills) { // Get video still URL. // Include view tracking parameters if present. sanitizedReplacementUrl = buildYoutubeVideoStillURL(decodedUrl) + decodedUrl.viewTrackingParameters; @@ -215,14 +217,13 @@ public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { try { final int responseCode = responseInfo.getHttpStatusCode(); if (responseCode != 200) { - AlternativeThumbnailMode currentMode = AlternativeThumbnailMode.getCurrent(); String url = responseInfo.getUrl(); - if (currentMode.usingDeArrow() && urlIsDeArrow(url)) { + if (usingDeArrow() && urlIsDeArrow(url)) { handleDeArrowError(responseInfo); } - if (currentMode.usingVideoStills() && responseCode == 404) { + if (usingVideoStills() && responseCode == 404) { // Fast alt thumbnails is enabled and the thumbnail is not available. // The video is: // - live stream @@ -258,9 +259,8 @@ public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { public static void handleCronetFailure(@Nullable UrlResponseInfo responseInfo, IOException exception) { try { LogHelper.printDebug(() -> "handleCronetFailure exception: " + exception); - AlternativeThumbnailMode currentMode = AlternativeThumbnailMode.getCurrent(); - if (currentMode.usingDeArrow()) { + if (usingDeArrow()) { // If the DeArrow API host name does not resolve, then the response is nbull // and the IOException (CronetException) provides no information to detect this situation. // For this situation no error toast is shown and no API backoff is done, @@ -324,7 +324,7 @@ static ThumbnailQuality getQualityToUse(@NonNull String originalSize) { return null; // Not a thumbnail for a regular video. } - final boolean useFastQuality = SettingsEnum.ALT_THUMBNAIL_STILL_FAST_QUALITY.getBoolean(); + final boolean useFastQuality = SettingsEnum.ALT_THUMBNAIL_STILLS_FAST.getBoolean(); switch (quality) { case SDDEFAULT: // SD alt images have somewhat worse quality with washed out color and poor contrast. @@ -359,7 +359,7 @@ static ThumbnailQuality getQualityToUse(@NonNull String originalSize) { } String getAltImageNameToUse() { - return altImageName + SettingsEnum.ALT_THUMBNAIL_STILL_TIME.getInt(); + return altImageName + SettingsEnum.ALT_THUMBNAIL_STILLS_TIME.getInt(); } } @@ -404,7 +404,7 @@ private static VerifiedQualities getVerifiedQualities(@NonNull String videoId, b static boolean verifyAltThumbnailExist(@NonNull String videoId, @NonNull ThumbnailQuality quality, @NonNull String imageUrl) { - VerifiedQualities verified = getVerifiedQualities(videoId, SettingsEnum.ALT_THUMBNAIL_STILL_FAST_QUALITY.getBoolean()); + VerifiedQualities verified = getVerifiedQualities(videoId, SettingsEnum.ALT_THUMBNAIL_STILLS_FAST.getBoolean()); if (verified == null) return true; // Fast alt thumbnails is enabled. return verified.verifyYouTubeThumbnailExists(videoId, quality, imageUrl); } @@ -455,7 +455,7 @@ synchronized boolean verifyYouTubeThumbnailExists(@NonNull String videoId, @NonN return true; // Previously verified as existing. } - final boolean fastQuality = SettingsEnum.ALT_THUMBNAIL_STILL_FAST_QUALITY.getBoolean(); + final boolean fastQuality = SettingsEnum.ALT_THUMBNAIL_STILLS_FAST.getBoolean(); if (lowestQualityNotAvailable != null && lowestQualityNotAvailable.ordinal() <= quality.ordinal()) { if (fastQuality || System.currentTimeMillis() < timeToReVerifyLowestQuality) { return false; // Previously verified as not existing. @@ -558,7 +558,7 @@ private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEn ? "" : fullUrl.substring(imageExtensionEndIndex); } - String createStillUrl(@NonNull ThumbnailQuality qualityToUse, boolean includeViewTracking) { + String createStillsUrl(@NonNull ThumbnailQuality qualityToUse, boolean includeViewTracking) { // Images could be upgraded to webp if they are not already, but this fails quite often, // especially for new videos uploaded in the last hour. // And even if alt webp images do exist, sometimes they can load much slower than the original jpg alt images. @@ -574,62 +574,4 @@ String createStillUrl(@NonNull ThumbnailQuality qualityToUse, boolean includeVie return builder.toString(); } } - - /** - * Alternative thumbnail mode. - */ - private enum AlternativeThumbnailMode { - /** - * Use the original thumbnails provided by the content creator. - * This effectively disables the patch, as this options matches the stock behaviour. - */ - ORIGINAL(1), - - /** - * Use video stills provided by YouTube. - * Uses {@link SettingsEnum#ALT_THUMBNAIL_STILL_TIME} and {@link SettingsEnum#ALT_THUMBNAIL_STILL_FAST_QUALITY}. - */ - VIDEO_STILLS(2), - - /** - * Use thumbnails provided by DeArrow, fallback to {@link AlternativeThumbnailMode#ORIGINAL}. - */ - DEARROW_OR_CREATOR_PROVIDED(3), - - /** - * Use thumbnails provided by DeArrow, fallback to {@link AlternativeThumbnailMode#VIDEO_STILLS}. - */ - DEARROW_OR_VIDEO_STILLS(4); - - private final int id; - AlternativeThumbnailMode(int id) { - this.id = id; - } - - public boolean usingVideoStills() { - return this == VIDEO_STILLS || this == DEARROW_OR_VIDEO_STILLS; - } - - public boolean usingDeArrow() { - return this == DEARROW_OR_CREATOR_PROVIDED || this == DEARROW_OR_VIDEO_STILLS; - } - - @NonNull - public static AlternativeThumbnailMode getCurrent() { - final var id = SettingsEnum.ALT_THUMBNAIL_MODE.getInt(); - // Could use the Enum ordinal and use values()[id], - // but then the ids would start at 0. - // Since only 4 ids exist this isn't a big deal - // and little overhead is needed to manually compare 4 int values. - for (final var mode : AlternativeThumbnailMode.values()) { - if (mode.id == id) { - return mode; - } - } - // User imported bad data and did not restart the app. Fix the settings and continue. - validateSettings(); - return getCurrent(); - } - - } } diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index ae50d1e779..45d110a7c4 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -56,13 +56,12 @@ public enum SettingsEnum { HIDE_WEB_SEARCH_RESULTS("revanced_hide_web_search_results", BOOLEAN, TRUE), // Layout - ALT_THUMBNAIL_MODE("revanced_alt_thumbnail_mode", INTEGER, 1), - @Deprecated DEPRECATED_ALT_THUMBNAIL_TYPE("revanced_alt_thumbnail_type", INTEGER, 2), - ALT_THUMBNAIL_STILL_TIME("revanced_alt_thumbnail_still_time", INTEGER, 2), - @Deprecated DEPRECATED_ALT_THUMBNAIL_FAST_QUALITY("revanced_alt_thumbnail_fast_quality", BOOLEAN, FALSE), - ALT_THUMBNAIL_STILL_FAST_QUALITY("revanced_alt_thumbnail_still_fast_quality", BOOLEAN, FALSE), + ALT_THUMBNAIL_STILLS("revanced_alt_thumbnail_stills", BOOLEAN, FALSE), + ALT_THUMBNAIL_STILLS_TIME("revanced_alt_thumbnail_stills_time", INTEGER, 2, parents(ALT_THUMBNAIL_STILLS)), + ALT_THUMBNAIL_STILLS_FAST("revanced_alt_thumbnail_stills_fast", BOOLEAN, FALSE, parents(ALT_THUMBNAIL_STILLS)), + ALT_THUMBNAIL_DEARROW("revanced_alt_thumbnail_dearrow", BOOLEAN, false), ALT_THUMBNAIL_DEARROW_API_URL("revanced_alt_thumbnail_dearrow_api_url", STRING, - "https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true), + "https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true, parents(ALT_THUMBNAIL_DEARROW)), CUSTOM_FILTER("revanced_custom_filter", BOOLEAN, FALSE), CUSTOM_FILTER_STRINGS("revanced_custom_filter_strings", STRING, "", true, parents(CUSTOM_FILTER)), DISABLE_FULLSCREEN_AMBIENT_MODE("revanced_disable_fullscreen_ambient_mode", BOOLEAN, TRUE, true), @@ -407,8 +406,6 @@ private static void loadAllSettings() { migrateOldSettingToNew(DISABLE_FINE_SCRUBBING_GESTURE, DISABLE_PRECISE_SEEKING_GESTURE); migrateOldSettingToNew(SHOW_OLD_VIDEO_QUALITY_MENU, RESTORE_OLD_VIDEO_QUALITY_MENU); migrateOldSettingToNew(ENABLE_OLD_SEEKBAR_THUMBNAILS, RESTORE_OLD_SEEKBAR_THUMBNAILS); - migrateOldSettingToNew(DEPRECATED_ALT_THUMBNAIL_TYPE, ALT_THUMBNAIL_STILL_TIME); - migrateOldSettingToNew(DEPRECATED_ALT_THUMBNAIL_FAST_QUALITY, ALT_THUMBNAIL_STILL_FAST_QUALITY); // Do _not_ delete this SB private user id migration property until sometime in 2024. // This is the only setting that cannot be reconfigured if lost, diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutPreference.java b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutPreference.java new file mode 100644 index 0000000000..725c624e4e --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutPreference.java @@ -0,0 +1,96 @@ +package app.revanced.integrations.settingsmenu; + +import static app.revanced.integrations.utils.StringRef.str; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.preference.Preference; +import android.util.AttributeSet; + +import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.settings.SharedPrefCategory; +import app.revanced.integrations.utils.ReVancedUtils; + +/** + * Shows what thumbnails will be used based on the current settings. + */ +@SuppressWarnings("unused") +public class AlternativeThumbnailsAboutPreference extends Preference { + + private SharedPreferences.OnSharedPreferenceChangeListener listener; + + private void init() { + setOnPreferenceClickListener(pref -> { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://dearrow.ajay.app")); + pref.getContext().startActivity(i); + return false; + }); + + updateUI(); + + 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 complete and then the settings are up to date. + ReVancedUtils.runOnMainThread(this::updateUI); + }; + SharedPrefCategory.YOUTUBE.preferences.registerOnSharedPreferenceChangeListener(listener); + } + + private void removeChangeListener() { + SharedPrefCategory.YOUTUBE.preferences.unregisterOnSharedPreferenceChangeListener(listener); + } + + public AlternativeThumbnailsAboutPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(); + } + public AlternativeThumbnailsAboutPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + public AlternativeThumbnailsAboutPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + public AlternativeThumbnailsAboutPreference(Context context) { + super(context); + init(); + } + + @Override + protected void onPrepareForRemoval() { + super.onPrepareForRemoval(); + + removeChangeListener(); + } + + private void updateUI() { + final boolean usingVideoStills = SettingsEnum.ALT_THUMBNAIL_STILLS.getBoolean(); + final boolean usingDeArrow = SettingsEnum.ALT_THUMBNAIL_DEARROW.getBoolean(); + + final String summaryText; + boolean isSelectable = false; + if (usingDeArrow) { + isSelectable = true; + String summaryKey = "revanced_alt_thumbnail_about_summary_dearrow"; + String additionalSummaryText = str(usingVideoStills + ? "revanced_alt_thumbnail_about_summary_dearrow_fallback_stills" + : "revanced_alt_thumbnail_about_summary_dearrow_fallback_none"); + String webLinkDescription = str("revanced_alt_thumbnail_about_summary_link_text"); + summaryText = str(summaryKey, additionalSummaryText, webLinkDescription); + } else if (usingVideoStills) { + summaryText = str("revanced_alt_thumbnail_about_summary_stills"); + } else { + summaryText = str("revanced_alt_thumbnail_about_summary_disabled"); + } + + setSummary(summaryText); + setSelectable(isSelectable); + } +} From bff6bf79187f67c4597d0d591e2058d33bfbcea6 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Thu, 7 Dec 2023 01:01:00 +0400 Subject: [PATCH 18/37] DeArrow toast option. Correctly handle all relevant connection errors --- .../patches/AlternativeThumbnailsPatch.java | 73 +++++++++++++------ .../integrations/settings/SettingsEnum.java | 1 + .../java/org/chromium/net/UrlRequest.java | 4 + .../org/chromium/net/UrlResponseInfo.java | 2 - .../chromium/net/impl/CronetUrlRequest.java | 13 ++++ 5 files changed, 70 insertions(+), 23 deletions(-) create mode 100644 dummy/src/main/java/org/chromium/net/UrlRequest.java create mode 100644 dummy/src/main/java/org/chromium/net/impl/CronetUrlRequest.java diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index cc574283ab..a43e4cde7e 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -1,12 +1,16 @@ package app.revanced.integrations.patches; +import static app.revanced.integrations.utils.StringRef.str; + import android.net.Uri; import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.chromium.net.UrlRequest; import org.chromium.net.UrlResponseInfo; +import org.chromium.net.impl.CronetUrlRequest; import java.io.IOException; import java.net.HttpURLConnection; @@ -56,16 +60,30 @@ public final class AlternativeThumbnailsPatch { /** * How long to temporarily turn off DeArrow if it fails for any reason. */ - private static final long DEARROW_CONNECTION_FAILURE_BACKOFF_MILLISECONDS = 2 * 60 * 1000; // 2 Minutes. + private static final long DEARROW_CONNECTION_FAILURE_API_BACKOFF_MILLISECONDS = 2 * 60 * 1000; // 2 Minutes. /** * If non zero, then the system time of when DeArrow API calls can resume. */ private static volatile long timeToResumeDeArrowAPICalls; + /** + * How long to temporarily turn off connection failure toasts if DeArrow fails for any reason. + */ + private static final long DEARROW_CONNECTION_FAILURE_TOAST_BACKOFF_MILLISECONDS = 60 * 60 * 1000; // 60 Minutes. + + /** + * If non zero, then the system time of when connection error toasts can be shown again. + * Differs from {@link #DEARROW_CONNECTION_FAILURE_API_BACKOFF_MILLISECONDS}. + */ + private static volatile long timeToResumeDeArrowConnectionToasts; + static { dearrowApiUri = validateSettings(); - deArrowApiUrlPrefix = dearrowApiUri.getScheme() + "://" + dearrowApiUri.getHost() + "/"; + final int port = dearrowApiUri.getPort(); + String portString = port == -1 ? "" : (":" + port); + deArrowApiUrlPrefix = dearrowApiUri.getScheme() + "://" + dearrowApiUri.getHost() + portString + "/"; + LogHelper.printDebug(() -> "Using DeArrow API address: " + deArrowApiUrlPrefix); } /** @@ -80,7 +98,8 @@ private static Uri validateSettings() { } Uri apiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); - if (apiUri.getScheme() == null || apiUri.getHost() == null) { + // Cannot use unsecured 'http', otherwise the connections fail to start and no callbacks hooks are made. + if (apiUri.getScheme() == null || apiUri.getScheme().equals("http") || apiUri.getHost() == null) { ReVancedUtils.showToastLong("Invalid DeArrow API URL. Using default"); SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.resetToDefault(); return validateSettings(); @@ -153,11 +172,17 @@ private static boolean canUseDeArrowAPI() { return false; } - private static void handleDeArrowError(UrlResponseInfo responseInfo) { - // TODO? Add a setting to show a toast on DeArrow failure? + private static void handleDeArrowError(@NonNull String url) { LogHelper.printDebug(() -> "Encountered DeArrow error. Backing off for " - + DEARROW_CONNECTION_FAILURE_BACKOFF_MILLISECONDS + "ms: " + responseInfo); - timeToResumeDeArrowAPICalls = System.currentTimeMillis() + DEARROW_CONNECTION_FAILURE_BACKOFF_MILLISECONDS; + + DEARROW_CONNECTION_FAILURE_API_BACKOFF_MILLISECONDS + "ms. Url: " + url); + final long now = System.currentTimeMillis(); + timeToResumeDeArrowAPICalls = now + DEARROW_CONNECTION_FAILURE_API_BACKOFF_MILLISECONDS; + + if (SettingsEnum.ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST.getBoolean() + && timeToResumeDeArrowConnectionToasts < now) { + ReVancedUtils.showToastLong(str("revanced_alt_thumbnail_dearrow_error_toast")); + } + timeToResumeDeArrowConnectionToasts = now + DEARROW_CONNECTION_FAILURE_TOAST_BACKOFF_MILLISECONDS; } /** @@ -213,14 +238,15 @@ public static String overrideImageURL(String originalUrl) { *

* Cronet considers all completed connections as a success, even if the response is 404 or 5xx. */ - public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { + public static void handleCronetSuccess(UrlRequest request, @NonNull UrlResponseInfo responseInfo) { try { final int responseCode = responseInfo.getHttpStatusCode(); if (responseCode != 200) { - String url = responseInfo.getUrl(); - + // Url is the still images url if DeArrow was not available. + String url = ((CronetUrlRequest) request).getHookedUrl(); if (usingDeArrow() && urlIsDeArrow(url)) { - handleDeArrowError(responseInfo); + LogHelper.printDebug(() -> "handleCronetSuccess responseCode: " + responseCode); + handleDeArrowError(url); } if (usingVideoStills() && responseCode == 404) { @@ -255,19 +281,24 @@ public static void handleCronetSuccess(@NonNull UrlResponseInfo responseInfo) { /** * Injection point. + * + * To test this hook try changing the API to each of: + * - A non existent domain + * - A url path of something incorrect (ie: /v1/nonExistentEndPoint) + * + * Known limitation: YT uses an infinite timeout, so this hook is never called if a host never responds. + * But this does not appear to be a problem, as the DeArrow API has not been observed to 'go silent' + * Instead if there's a problem it returns an error code status response, which this hook correctly handles. */ - public static void handleCronetFailure(@Nullable UrlResponseInfo responseInfo, IOException exception) { + public static void handleCronetFailure(UrlRequest request, + @Nullable UrlResponseInfo responseInfo, + IOException exception) { try { - LogHelper.printDebug(() -> "handleCronetFailure exception: " + exception); - if (usingDeArrow()) { - // If the DeArrow API host name does not resolve, then the response is nbull - // and the IOException (CronetException) provides no information to detect this situation. - // For this situation no error toast is shown and no API backoff is done, - // since this situation cannot be easily detected. Will not happen - // unless the user has changed the API url. - if (responseInfo != null && urlIsDeArrow(responseInfo.getUrl())) { - handleDeArrowError(responseInfo); + String url = ((CronetUrlRequest) request).getHookedUrl(); + if (urlIsDeArrow(url)) { + LogHelper.printDebug(() -> "handleCronetFailure exception: " + exception); + handleDeArrowError(url); } } } catch (Exception ex) { diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index 45d110a7c4..ad464cf21f 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -62,6 +62,7 @@ public enum SettingsEnum { ALT_THUMBNAIL_DEARROW("revanced_alt_thumbnail_dearrow", BOOLEAN, false), ALT_THUMBNAIL_DEARROW_API_URL("revanced_alt_thumbnail_dearrow_api_url", STRING, "https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true, parents(ALT_THUMBNAIL_DEARROW)), + ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST("revanced_alt_thumbnail_dearrow_connection_toast", BOOLEAN, TRUE, parents(ALT_THUMBNAIL_DEARROW)), CUSTOM_FILTER("revanced_custom_filter", BOOLEAN, FALSE), CUSTOM_FILTER_STRINGS("revanced_custom_filter_strings", STRING, "", true, parents(CUSTOM_FILTER)), DISABLE_FULLSCREEN_AMBIENT_MODE("revanced_disable_fullscreen_ambient_mode", BOOLEAN, TRUE, true), diff --git a/dummy/src/main/java/org/chromium/net/UrlRequest.java b/dummy/src/main/java/org/chromium/net/UrlRequest.java new file mode 100644 index 0000000000..565fc22274 --- /dev/null +++ b/dummy/src/main/java/org/chromium/net/UrlRequest.java @@ -0,0 +1,4 @@ +package org.chromium.net; + +public abstract class UrlRequest { +} diff --git a/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java b/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java index 31a75222b2..5f883bf221 100644 --- a/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java +++ b/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java @@ -3,8 +3,6 @@ //dummy class public abstract class UrlResponseInfo { - public abstract String getUrl(); // Can return NULL for some failures. - public abstract int getHttpStatusCode(); // Add additional existing methods, if needed. diff --git a/dummy/src/main/java/org/chromium/net/impl/CronetUrlRequest.java b/dummy/src/main/java/org/chromium/net/impl/CronetUrlRequest.java new file mode 100644 index 0000000000..0bae78ce08 --- /dev/null +++ b/dummy/src/main/java/org/chromium/net/impl/CronetUrlRequest.java @@ -0,0 +1,13 @@ +package org.chromium.net.impl; + +import org.chromium.net.UrlRequest; + +public abstract class CronetUrlRequest extends UrlRequest { + + /** + * Method is added by patches. + * + * @return the Url used by the request, and is the final url if the original was redirected. + */ + public abstract String getHookedUrl(); +} From 3ffe4dd1fb808aed7a7c73f2098154ff9ec71d49 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Thu, 7 Dec 2023 01:24:48 +0400 Subject: [PATCH 19/37] fixing oversight. The request url is not the same as the response if request was redirected. --- .../integrations/patches/AlternativeThumbnailsPatch.java | 3 +-- dummy/src/main/java/org/chromium/net/UrlResponseInfo.java | 2 ++ .../src/main/java/org/chromium/net/impl/CronetUrlRequest.java | 2 -- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index a43e4cde7e..124edefe92 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -242,8 +242,7 @@ public static void handleCronetSuccess(UrlRequest request, @NonNull UrlResponseI try { final int responseCode = responseInfo.getHttpStatusCode(); if (responseCode != 200) { - // Url is the still images url if DeArrow was not available. - String url = ((CronetUrlRequest) request).getHookedUrl(); + String url = responseInfo.getUrl(); if (usingDeArrow() && urlIsDeArrow(url)) { LogHelper.printDebug(() -> "handleCronetSuccess responseCode: " + responseCode); handleDeArrowError(url); diff --git a/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java b/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java index 5f883bf221..8e341247dc 100644 --- a/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java +++ b/dummy/src/main/java/org/chromium/net/UrlResponseInfo.java @@ -3,6 +3,8 @@ //dummy class public abstract class UrlResponseInfo { + public abstract String getUrl(); + public abstract int getHttpStatusCode(); // Add additional existing methods, if needed. diff --git a/dummy/src/main/java/org/chromium/net/impl/CronetUrlRequest.java b/dummy/src/main/java/org/chromium/net/impl/CronetUrlRequest.java index 0bae78ce08..b27a9659c6 100644 --- a/dummy/src/main/java/org/chromium/net/impl/CronetUrlRequest.java +++ b/dummy/src/main/java/org/chromium/net/impl/CronetUrlRequest.java @@ -6,8 +6,6 @@ public abstract class CronetUrlRequest extends UrlRequest { /** * Method is added by patches. - * - * @return the Url used by the request, and is the final url if the original was redirected. */ public abstract String getHookedUrl(); } From 054d7ffc9e89ece3672260060375bf8c43e24d33 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Thu, 7 Dec 2023 05:21:42 +0400 Subject: [PATCH 20/37] fix logging, comments, cleanup --- .../patches/AlternativeThumbnailsPatch.java | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 124edefe92..5abda38211 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -124,12 +124,13 @@ private static boolean usingVideoStills() { @NonNull private static String buildYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) { ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); - if (qualityToUse == null) return decodedUrl.sanitizedUrl; // Video is a short. + if (qualityToUse != null) { + String sanitizedReplacement = decodedUrl.createStillsUrl(qualityToUse, false); + if (VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { + return sanitizedReplacement; + } + } // else, video is a Short. - String sanitizedReplacement = decodedUrl.createStillsUrl(qualityToUse, false); - if (VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { - return sanitizedReplacement; - } return decodedUrl.sanitizedUrl; } @@ -203,13 +204,12 @@ public static String overrideImageURL(String originalUrl) { return originalUrl; // Not a thumbnail. } - // Keep any tracking parameters out of the logs, and log only the base URL. - LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); - String sanitizedReplacementUrl; + final boolean includeTracking; if (usingDeArrow && canUseDeArrowAPI()) { // Get fallback URL. // Do not include view tracking parameters with API call. + includeTracking = false; final String fallbackUrl = usingVideoStills ? buildYoutubeVideoStillURL(decodedUrl) : decodedUrl.sanitizedUrl; @@ -218,15 +218,19 @@ public static String overrideImageURL(String originalUrl) { } else if (usingVideoStills) { // Get video still URL. // Include view tracking parameters if present. - sanitizedReplacementUrl = buildYoutubeVideoStillURL(decodedUrl) + decodedUrl.viewTrackingParameters; + includeTracking = true; + sanitizedReplacementUrl = buildYoutubeVideoStillURL(decodedUrl); } else { return originalUrl; // Recently experienced DeArrow failure and video stills are not enabled. } - // Do not log the tracking parameters. - LogHelper.printDebug(() -> "Replacement url: " + sanitizedReplacementUrl); + // Do not log any tracking parameters. + LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl + + "\nReplacement url: " + sanitizedReplacementUrl); - return sanitizedReplacementUrl; + return includeTracking + ? sanitizedReplacementUrl + decodedUrl.viewTrackingParameters + : sanitizedReplacementUrl; } catch (Exception ex) { LogHelper.printException(() -> "overrideImageURL failure", ex); return originalUrl; @@ -243,9 +247,11 @@ public static void handleCronetSuccess(UrlRequest request, @NonNull UrlResponseI final int responseCode = responseInfo.getHttpStatusCode(); if (responseCode != 200) { String url = responseInfo.getUrl(); + if (usingDeArrow() && urlIsDeArrow(url)) { - LogHelper.printDebug(() -> "handleCronetSuccess responseCode: " + responseCode); + LogHelper.printDebug(() -> "handleCronetSuccess, responseCode: " + responseCode); handleDeArrowError(url); + return; } if (usingVideoStills() && responseCode == 404) { @@ -261,12 +267,12 @@ public static void handleCronetSuccess(UrlRequest request, @NonNull UrlResponseI return; // Not a thumbnail. } - LogHelper.printDebug(() -> "handleCronetSuccess responseCode: " + responseCode + " url: " + url); + LogHelper.printDebug(() -> "handleCronetSuccess, image not available: " + url); ThumbnailQuality quality = ThumbnailQuality.altImageNameToQuality(decodedUrl.imageQuality); if (quality == null) { - // Video is a short or unknown quality, but the url returned 404. Should never happen. - LogHelper.printDebug(() -> "Failed to load unknown url: " + decodedUrl.sanitizedUrl); + // Video is a short or unknown quality, but somehow did not load. Should not happen. + LogHelper.printDebug(() -> "Failed to recognize image quality of url: " + decodedUrl.sanitizedUrl); return; } @@ -296,7 +302,7 @@ public static void handleCronetFailure(UrlRequest request, if (usingDeArrow()) { String url = ((CronetUrlRequest) request).getHookedUrl(); if (urlIsDeArrow(url)) { - LogHelper.printDebug(() -> "handleCronetFailure exception: " + exception); + LogHelper.printDebug(() -> "handleCronetFailure, exception: " + exception); handleDeArrowError(url); } } @@ -305,6 +311,10 @@ public static void handleCronetFailure(UrlRequest request, } } + // Edit: YouTube now sometimes uses 'custom' screen captures in place of regular thumbnails. + // They appear with the format (original)_custom_(1-3), ie: "sddefault_custom_2.jpg" + // Presumably they are stills taken from video times chosen by the content creator. + // These could be replaced with a fixed time screen grab, for but now leave these as-is and don't replace. private enum ThumbnailQuality { // In order of lowest to highest resolution. DEFAULT("default", ""), // effective alt name is 1.jpg, 2.jpg, 3.jpg From b9172b85f900dd9eb2b9150cc9e0ddf25c0f90fc Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Thu, 7 Dec 2023 23:02:59 +0400 Subject: [PATCH 21/37] Prevent DeArrow 502 status error when it tries to fetch a storyboard image that is not accessible. --- .../patches/AlternativeThumbnailsPatch.java | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 5abda38211..3fcaafe040 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -99,7 +99,8 @@ private static Uri validateSettings() { Uri apiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); // Cannot use unsecured 'http', otherwise the connections fail to start and no callbacks hooks are made. - if (apiUri.getScheme() == null || apiUri.getScheme().equals("http") || apiUri.getHost() == null) { + String scheme = apiUri.getScheme(); + if (scheme == null || scheme.equals("http") || apiUri.getHost() == null) { ReVancedUtils.showToastLong("Invalid DeArrow API URL. Using default"); SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.resetToDefault(); return validateSettings(); @@ -122,15 +123,12 @@ private static boolean usingVideoStills() { * @return The alternative thumbnail url, or the original url. Both without tracking parameters. */ @NonNull - private static String buildYoutubeVideoStillURL(DecodedThumbnailUrl decodedUrl) { - ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); - if (qualityToUse != null) { - String sanitizedReplacement = decodedUrl.createStillsUrl(qualityToUse, false); - if (VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { - return sanitizedReplacement; - } - } // else, video is a Short. - + private static String buildYoutubeVideoStillURL(@NonNull DecodedThumbnailUrl decodedUrl, + @NonNull ThumbnailQuality qualityToUse) { + String sanitizedReplacement = decodedUrl.createStillsUrl(qualityToUse, false); + if (VerifiedQualities.verifyAltThumbnailExist(decodedUrl.videoId, qualityToUse, sanitizedReplacement)) { + return sanitizedReplacement; + } return decodedUrl.sanitizedUrl; } @@ -204,22 +202,27 @@ public static String overrideImageURL(String originalUrl) { return originalUrl; // Not a thumbnail. } + ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); + if (qualityToUse == null) { + // Thumbnail is: + // - Short + // - Custom video still capture chosen by the content creator + // - Storyboard image used for seekbar thumbnails (must not replace these) + return originalUrl; + } + String sanitizedReplacementUrl; final boolean includeTracking; if (usingDeArrow && canUseDeArrowAPI()) { - // Get fallback URL. - // Do not include view tracking parameters with API call. - includeTracking = false; + includeTracking = false; // Do not include view tracking parameters with API call. final String fallbackUrl = usingVideoStills - ? buildYoutubeVideoStillURL(decodedUrl) + ? buildYoutubeVideoStillURL(decodedUrl, qualityToUse) : decodedUrl.sanitizedUrl; sanitizedReplacementUrl = buildDeArrowThumbnailURL(decodedUrl.videoId, fallbackUrl); } else if (usingVideoStills) { - // Get video still URL. - // Include view tracking parameters if present. - includeTracking = true; - sanitizedReplacementUrl = buildYoutubeVideoStillURL(decodedUrl); + includeTracking = true; // Include view tracking parameters if present. + sanitizedReplacementUrl = buildYoutubeVideoStillURL(decodedUrl, qualityToUse); } else { return originalUrl; // Recently experienced DeArrow failure and video stills are not enabled. } @@ -287,13 +290,13 @@ public static void handleCronetSuccess(UrlRequest request, @NonNull UrlResponseI /** * Injection point. * - * To test this hook try changing the API to each of: - * - A non existent domain - * - A url path of something incorrect (ie: /v1/nonExistentEndPoint) + * To test failure cases, try changing the API URL to each of: + * - A non existent domain. + * - A url path of something incorrect (ie: /v1/nonExistentEndPoint). * * Known limitation: YT uses an infinite timeout, so this hook is never called if a host never responds. * But this does not appear to be a problem, as the DeArrow API has not been observed to 'go silent' - * Instead if there's a problem it returns an error code status response, which this hook correctly handles. + * Instead if there's a problem it returns an error code status response, which is handled in this patch. */ public static void handleCronetFailure(UrlRequest request, @Nullable UrlResponseInfo responseInfo, @@ -313,8 +316,8 @@ public static void handleCronetFailure(UrlRequest request, // Edit: YouTube now sometimes uses 'custom' screen captures in place of regular thumbnails. // They appear with the format (original)_custom_(1-3), ie: "sddefault_custom_2.jpg" - // Presumably they are stills taken from video times chosen by the content creator. - // These could be replaced with a fixed time screen grab, for but now leave these as-is and don't replace. + // Presumably they are still captures using video times chosen by the content creator. + // Could replaced them with a fixed time screen grab, for but now leave these as-is and don't replace. private enum ThumbnailQuality { // In order of lowest to highest resolution. DEFAULT("default", ""), // effective alt name is 1.jpg, 2.jpg, 3.jpg From 104737295f27e9bcbd9cf914a60702d8e2e39ad6 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Thu, 7 Dec 2023 23:03:47 +0400 Subject: [PATCH 22/37] Adjust backoff and use api timeout for toast backoff as well. --- .../patches/AlternativeThumbnailsPatch.java | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 3fcaafe040..7559083e4d 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -60,24 +60,13 @@ public final class AlternativeThumbnailsPatch { /** * How long to temporarily turn off DeArrow if it fails for any reason. */ - private static final long DEARROW_CONNECTION_FAILURE_API_BACKOFF_MILLISECONDS = 2 * 60 * 1000; // 2 Minutes. + private static final long DEARROW_FAILURE_API_BACKOFF_MILLISECONDS = 5 * 60 * 1000; // 5 Minutes. /** * If non zero, then the system time of when DeArrow API calls can resume. */ private static volatile long timeToResumeDeArrowAPICalls; - /** - * How long to temporarily turn off connection failure toasts if DeArrow fails for any reason. - */ - private static final long DEARROW_CONNECTION_FAILURE_TOAST_BACKOFF_MILLISECONDS = 60 * 60 * 1000; // 60 Minutes. - - /** - * If non zero, then the system time of when connection error toasts can be shown again. - * Differs from {@link #DEARROW_CONNECTION_FAILURE_API_BACKOFF_MILLISECONDS}. - */ - private static volatile long timeToResumeDeArrowConnectionToasts; - static { dearrowApiUri = validateSettings(); final int port = dearrowApiUri.getPort(); @@ -163,7 +152,7 @@ private static boolean canUseDeArrowAPI() { if (timeToResumeDeArrowAPICalls == 0) { return true; } - if (System.currentTimeMillis() > timeToResumeDeArrowAPICalls) { + if (timeToResumeDeArrowAPICalls < System.currentTimeMillis()) { LogHelper.printDebug(() -> "Resuming DeArrow API calls"); timeToResumeDeArrowAPICalls = 0; return true; @@ -172,16 +161,14 @@ private static boolean canUseDeArrowAPI() { } private static void handleDeArrowError(@NonNull String url) { - LogHelper.printDebug(() -> "Encountered DeArrow error. Backing off for " - + DEARROW_CONNECTION_FAILURE_API_BACKOFF_MILLISECONDS + "ms. Url: " + url); final long now = System.currentTimeMillis(); - timeToResumeDeArrowAPICalls = now + DEARROW_CONNECTION_FAILURE_API_BACKOFF_MILLISECONDS; - - if (SettingsEnum.ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST.getBoolean() - && timeToResumeDeArrowConnectionToasts < now) { - ReVancedUtils.showToastLong(str("revanced_alt_thumbnail_dearrow_error_toast")); + LogHelper.printDebug(() -> "Encountered DeArrow error. Url: " + url); + if (timeToResumeDeArrowAPICalls < now) { + timeToResumeDeArrowAPICalls = now + DEARROW_FAILURE_API_BACKOFF_MILLISECONDS; + if (SettingsEnum.ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST.getBoolean()) { + ReVancedUtils.showToastLong(str("revanced_alt_thumbnail_dearrow_error_toast")); + } } - timeToResumeDeArrowConnectionToasts = now + DEARROW_CONNECTION_FAILURE_TOAST_BACKOFF_MILLISECONDS; } /** @@ -206,7 +193,7 @@ public static String overrideImageURL(String originalUrl) { if (qualityToUse == null) { // Thumbnail is: // - Short - // - Custom video still capture chosen by the content creator + // - Custom video still capture chosen by the content creator ('_custom_' image) // - Storyboard image used for seekbar thumbnails (must not replace these) return originalUrl; } From e87e344faef0d5b3c0aef9e35360beb8fe85bdb6 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 8 Dec 2023 00:42:06 +0400 Subject: [PATCH 23/37] Use separate about section for each thumbnail type. --- .../patches/AlternativeThumbnailsPatch.java | 22 ++--- .../integrations/settings/SettingsEnum.java | 14 +-- ...ativeThumbnailsAboutDeArrowPreference.java | 41 ++++++++ .../AlternativeThumbnailsAboutPreference.java | 96 ------------------- ...AlternativeThumbnailsStatusPreference.java | 86 +++++++++++++++++ .../chromium/net/impl/CronetUrlRequest.java | 2 +- 6 files changed, 146 insertions(+), 115 deletions(-) create mode 100644 app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutDeArrowPreference.java delete mode 100644 app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutPreference.java create mode 100644 app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 7559083e4d..495d7f6712 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -79,30 +79,30 @@ public final class AlternativeThumbnailsPatch { * Fix any bad imported data. */ private static Uri validateSettings() { - final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_STILLS_TIME.getInt(); + final int altThumbnailType = SettingsEnum.ALT_THUMBNAILS_STILLS_TIME.getInt(); if (altThumbnailType < 1 || altThumbnailType > 3) { ReVancedUtils.showToastLong("Invalid Alternative still thumbnail type: " + altThumbnailType + ". Using default"); - SettingsEnum.ALT_THUMBNAIL_STILLS_TIME.resetToDefault(); + SettingsEnum.ALT_THUMBNAILS_STILLS_TIME.resetToDefault(); } - Uri apiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); + Uri apiUri = Uri.parse(SettingsEnum.ALT_THUMBNAILS_DEARROW_API_URL.getString()); // Cannot use unsecured 'http', otherwise the connections fail to start and no callbacks hooks are made. String scheme = apiUri.getScheme(); if (scheme == null || scheme.equals("http") || apiUri.getHost() == null) { ReVancedUtils.showToastLong("Invalid DeArrow API URL. Using default"); - SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.resetToDefault(); + SettingsEnum.ALT_THUMBNAILS_DEARROW_API_URL.resetToDefault(); return validateSettings(); } return apiUri; } private static boolean usingDeArrow() { - return SettingsEnum.ALT_THUMBNAIL_DEARROW.getBoolean(); + return SettingsEnum.ALT_THUMBNAILS_DEARROW.getBoolean(); } private static boolean usingVideoStills() { - return SettingsEnum.ALT_THUMBNAIL_STILLS.getBoolean(); + return SettingsEnum.ALT_THUMBNAILS_STILLS.getBoolean(); } /** @@ -165,7 +165,7 @@ private static void handleDeArrowError(@NonNull String url) { LogHelper.printDebug(() -> "Encountered DeArrow error. Url: " + url); if (timeToResumeDeArrowAPICalls < now) { timeToResumeDeArrowAPICalls = now + DEARROW_FAILURE_API_BACKOFF_MILLISECONDS; - if (SettingsEnum.ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST.getBoolean()) { + if (SettingsEnum.ALT_THUMBNAILS_DEARROW_CONNECTION_TOAST.getBoolean()) { ReVancedUtils.showToastLong(str("revanced_alt_thumbnail_dearrow_error_toast")); } } @@ -354,7 +354,7 @@ static ThumbnailQuality getQualityToUse(@NonNull String originalSize) { return null; // Not a thumbnail for a regular video. } - final boolean useFastQuality = SettingsEnum.ALT_THUMBNAIL_STILLS_FAST.getBoolean(); + final boolean useFastQuality = SettingsEnum.ALT_THUMBNAILS_STILLS_FAST.getBoolean(); switch (quality) { case SDDEFAULT: // SD alt images have somewhat worse quality with washed out color and poor contrast. @@ -389,7 +389,7 @@ static ThumbnailQuality getQualityToUse(@NonNull String originalSize) { } String getAltImageNameToUse() { - return altImageName + SettingsEnum.ALT_THUMBNAIL_STILLS_TIME.getInt(); + return altImageName + SettingsEnum.ALT_THUMBNAILS_STILLS_TIME.getInt(); } } @@ -434,7 +434,7 @@ private static VerifiedQualities getVerifiedQualities(@NonNull String videoId, b static boolean verifyAltThumbnailExist(@NonNull String videoId, @NonNull ThumbnailQuality quality, @NonNull String imageUrl) { - VerifiedQualities verified = getVerifiedQualities(videoId, SettingsEnum.ALT_THUMBNAIL_STILLS_FAST.getBoolean()); + VerifiedQualities verified = getVerifiedQualities(videoId, SettingsEnum.ALT_THUMBNAILS_STILLS_FAST.getBoolean()); if (verified == null) return true; // Fast alt thumbnails is enabled. return verified.verifyYouTubeThumbnailExists(videoId, quality, imageUrl); } @@ -485,7 +485,7 @@ synchronized boolean verifyYouTubeThumbnailExists(@NonNull String videoId, @NonN return true; // Previously verified as existing. } - final boolean fastQuality = SettingsEnum.ALT_THUMBNAIL_STILLS_FAST.getBoolean(); + final boolean fastQuality = SettingsEnum.ALT_THUMBNAILS_STILLS_FAST.getBoolean(); if (lowestQualityNotAvailable != null && lowestQualityNotAvailable.ordinal() <= quality.ordinal()) { if (fastQuality || System.currentTimeMillis() < timeToReVerifyLowestQuality) { return false; // Previously verified as not existing. diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index ad464cf21f..4573426cd7 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -56,13 +56,13 @@ public enum SettingsEnum { HIDE_WEB_SEARCH_RESULTS("revanced_hide_web_search_results", BOOLEAN, TRUE), // Layout - ALT_THUMBNAIL_STILLS("revanced_alt_thumbnail_stills", BOOLEAN, FALSE), - ALT_THUMBNAIL_STILLS_TIME("revanced_alt_thumbnail_stills_time", INTEGER, 2, parents(ALT_THUMBNAIL_STILLS)), - ALT_THUMBNAIL_STILLS_FAST("revanced_alt_thumbnail_stills_fast", BOOLEAN, FALSE, parents(ALT_THUMBNAIL_STILLS)), - ALT_THUMBNAIL_DEARROW("revanced_alt_thumbnail_dearrow", BOOLEAN, false), - ALT_THUMBNAIL_DEARROW_API_URL("revanced_alt_thumbnail_dearrow_api_url", STRING, - "https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true, parents(ALT_THUMBNAIL_DEARROW)), - ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST("revanced_alt_thumbnail_dearrow_connection_toast", BOOLEAN, TRUE, parents(ALT_THUMBNAIL_DEARROW)), + ALT_THUMBNAILS_STILLS("revanced_alt_thumbnails_stills", BOOLEAN, FALSE), + ALT_THUMBNAILS_STILLS_TIME("revanced_alt_thumbnails_stills_time", INTEGER, 2, parents(ALT_THUMBNAILS_STILLS)), + ALT_THUMBNAILS_STILLS_FAST("revanced_alt_thumbnails_stills_fast", BOOLEAN, FALSE, parents(ALT_THUMBNAILS_STILLS)), + ALT_THUMBNAILS_DEARROW("revanced_alt_thumbnails_dearrow", BOOLEAN, false), + ALT_THUMBNAILS_DEARROW_API_URL("revanced_alt_thumbnails_dearrow_api_url", STRING, + "https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true, parents(ALT_THUMBNAILS_DEARROW)), + ALT_THUMBNAILS_DEARROW_CONNECTION_TOAST("revanced_alt_thumbnails_dearrow_connection_toast", BOOLEAN, TRUE, parents(ALT_THUMBNAILS_DEARROW)), CUSTOM_FILTER("revanced_custom_filter", BOOLEAN, FALSE), CUSTOM_FILTER_STRINGS("revanced_custom_filter_strings", STRING, "", true, parents(CUSTOM_FILTER)), DISABLE_FULLSCREEN_AMBIENT_MODE("revanced_disable_fullscreen_ambient_mode", BOOLEAN, TRUE, true), diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutDeArrowPreference.java b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutDeArrowPreference.java new file mode 100644 index 0000000000..f4e0ebf306 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutDeArrowPreference.java @@ -0,0 +1,41 @@ +package app.revanced.integrations.settingsmenu; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.preference.Preference; +import android.util.AttributeSet; + +/** + * Allows tapping the DeArrow about preference to open the DeArrow website. + */ +@SuppressWarnings("unused") +public class AlternativeThumbnailsAboutDeArrowPreference extends Preference { + + private void init() { + setSelectable(true); + setOnPreferenceClickListener(pref -> { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://dearrow.ajay.app")); + pref.getContext().startActivity(i); + return false; + }); + } + + public AlternativeThumbnailsAboutDeArrowPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(); + } + public AlternativeThumbnailsAboutDeArrowPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + public AlternativeThumbnailsAboutDeArrowPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + public AlternativeThumbnailsAboutDeArrowPreference(Context context) { + super(context); + init(); + } +} diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutPreference.java b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutPreference.java deleted file mode 100644 index 725c624e4e..0000000000 --- a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutPreference.java +++ /dev/null @@ -1,96 +0,0 @@ -package app.revanced.integrations.settingsmenu; - -import static app.revanced.integrations.utils.StringRef.str; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.Uri; -import android.preference.Preference; -import android.util.AttributeSet; - -import app.revanced.integrations.settings.SettingsEnum; -import app.revanced.integrations.settings.SharedPrefCategory; -import app.revanced.integrations.utils.ReVancedUtils; - -/** - * Shows what thumbnails will be used based on the current settings. - */ -@SuppressWarnings("unused") -public class AlternativeThumbnailsAboutPreference extends Preference { - - private SharedPreferences.OnSharedPreferenceChangeListener listener; - - private void init() { - setOnPreferenceClickListener(pref -> { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse("https://dearrow.ajay.app")); - pref.getContext().startActivity(i); - return false; - }); - - updateUI(); - - 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 complete and then the settings are up to date. - ReVancedUtils.runOnMainThread(this::updateUI); - }; - SharedPrefCategory.YOUTUBE.preferences.registerOnSharedPreferenceChangeListener(listener); - } - - private void removeChangeListener() { - SharedPrefCategory.YOUTUBE.preferences.unregisterOnSharedPreferenceChangeListener(listener); - } - - public AlternativeThumbnailsAboutPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); - } - public AlternativeThumbnailsAboutPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - public AlternativeThumbnailsAboutPreference(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - public AlternativeThumbnailsAboutPreference(Context context) { - super(context); - init(); - } - - @Override - protected void onPrepareForRemoval() { - super.onPrepareForRemoval(); - - removeChangeListener(); - } - - private void updateUI() { - final boolean usingVideoStills = SettingsEnum.ALT_THUMBNAIL_STILLS.getBoolean(); - final boolean usingDeArrow = SettingsEnum.ALT_THUMBNAIL_DEARROW.getBoolean(); - - final String summaryText; - boolean isSelectable = false; - if (usingDeArrow) { - isSelectable = true; - String summaryKey = "revanced_alt_thumbnail_about_summary_dearrow"; - String additionalSummaryText = str(usingVideoStills - ? "revanced_alt_thumbnail_about_summary_dearrow_fallback_stills" - : "revanced_alt_thumbnail_about_summary_dearrow_fallback_none"); - String webLinkDescription = str("revanced_alt_thumbnail_about_summary_link_text"); - summaryText = str(summaryKey, additionalSummaryText, webLinkDescription); - } else if (usingVideoStills) { - summaryText = str("revanced_alt_thumbnail_about_summary_stills"); - } else { - summaryText = str("revanced_alt_thumbnail_about_summary_disabled"); - } - - setSummary(summaryText); - setSelectable(isSelectable); - } -} diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java new file mode 100644 index 0000000000..7b03a395fb --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java @@ -0,0 +1,86 @@ +package app.revanced.integrations.settingsmenu; + +import static app.revanced.integrations.utils.StringRef.str; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.Preference; +import android.preference.PreferenceManager; +import android.util.AttributeSet; + +import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.settings.SharedPrefCategory; +import app.revanced.integrations.utils.LogHelper; +import app.revanced.integrations.utils.ReVancedUtils; + +/** + * 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) -> { + if (str.equals(SettingsEnum.ALT_THUMBNAILS_DEARROW.path) + || str.equals(SettingsEnum.ALT_THUMBNAILS_STILLS.path)) { + // 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 complete and then the settings are up to date. + ReVancedUtils.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() { + SharedPrefCategory.YOUTUBE.preferences.registerOnSharedPreferenceChangeListener(listener); + } + + private void removeChangeListener() { + SharedPrefCategory.YOUTUBE.preferences.unregisterOnSharedPreferenceChangeListener(listener); + } + + @Override + protected void onAttachedToHierarchy(PreferenceManager preferenceManager) { + super.onAttachedToHierarchy(preferenceManager); + updateUI(); + addChangeListener(); + } + + @Override + protected void onPrepareForRemoval() { + super.onPrepareForRemoval(); + removeChangeListener(); + } + + private void updateUI() { + LogHelper.printDebug(() -> "updateUI"); + final boolean usingVideoStills = SettingsEnum.ALT_THUMBNAILS_STILLS.getBoolean(); + final boolean usingDeArrow = SettingsEnum.ALT_THUMBNAILS_DEARROW.getBoolean(); + + final String summaryTextKey; + if (usingDeArrow && usingVideoStills) { + summaryTextKey = "revanced_alt_thumbnails_about_status_dearrow_stills"; + } else if (usingDeArrow) { + summaryTextKey = "revanced_alt_thumbnails_about_status_dearrow"; + } else if (usingVideoStills) { + summaryTextKey = "revanced_alt_thumbnails_about_status_stills"; + } else { + summaryTextKey = "revanced_alt_thumbnails_about_status_disabled"; + } + + setSummary(str(summaryTextKey)); + } +} diff --git a/dummy/src/main/java/org/chromium/net/impl/CronetUrlRequest.java b/dummy/src/main/java/org/chromium/net/impl/CronetUrlRequest.java index b27a9659c6..fa0dcacd98 100644 --- a/dummy/src/main/java/org/chromium/net/impl/CronetUrlRequest.java +++ b/dummy/src/main/java/org/chromium/net/impl/CronetUrlRequest.java @@ -5,7 +5,7 @@ public abstract class CronetUrlRequest extends UrlRequest { /** - * Method is added by patches. + * Method is added by patch. */ public abstract String getHookedUrl(); } From 5ad69e2dd9dc07e5b00848343cf5ad7c18a5703e Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:50:17 +0400 Subject: [PATCH 24/37] custom thumbnails can be any kind of image. Replace them if found. --- .../patches/AlternativeThumbnailsPatch.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 495d7f6712..b6ab75f847 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -189,12 +189,11 @@ public static String overrideImageURL(String originalUrl) { return originalUrl; // Not a thumbnail. } + LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl); + ThumbnailQuality qualityToUse = ThumbnailQuality.getQualityToUse(decodedUrl.imageQuality); if (qualityToUse == null) { - // Thumbnail is: - // - Short - // - Custom video still capture chosen by the content creator ('_custom_' image) - // - Storyboard image used for seekbar thumbnails (must not replace these) + // Thumbnail is a Short or a Storyboard image used for seekbar thumbnails (must not replace these). return originalUrl; } @@ -215,8 +214,7 @@ public static String overrideImageURL(String originalUrl) { } // Do not log any tracking parameters. - LogHelper.printDebug(() -> "Original url: " + decodedUrl.sanitizedUrl - + "\nReplacement url: " + sanitizedReplacementUrl); + LogHelper.printDebug(() -> "Replacement url: " + sanitizedReplacementUrl); return includeTracking ? sanitizedReplacementUrl + decodedUrl.viewTrackingParameters @@ -301,10 +299,6 @@ public static void handleCronetFailure(UrlRequest request, } } - // Edit: YouTube now sometimes uses 'custom' screen captures in place of regular thumbnails. - // They appear with the format (original)_custom_(1-3), ie: "sddefault_custom_2.jpg" - // Presumably they are still captures using video times chosen by the content creator. - // Could replaced them with a fixed time screen grab, for but now leave these as-is and don't replace. private enum ThumbnailQuality { // In order of lowest to highest resolution. DEFAULT("default", ""), // effective alt name is 1.jpg, 2.jpg, 3.jpg @@ -329,6 +323,11 @@ private enum ThumbnailQuality { originalNameToEnum.put(quality.originalName, quality); for (int i = 1; i <= 3; i++) { + // 'custom' thumbnails set by the content creator. + // These show up in place of regular thumbnails + // and seem to be limited to [1, 3] range. + originalNameToEnum.put(quality.originalName + "_custom_" + i, quality); + altNameToEnum.put(quality.altImageName + i, quality); } } From f28ef9c7f3c4d55955ffa43e54a987c45c791267 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:25:04 +0400 Subject: [PATCH 25/37] simplify logic --- .../integrations/patches/AlternativeThumbnailsPatch.java | 9 +++------ .../AlternativeThumbnailsAboutDeArrowPreference.java | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index b6ab75f847..47af40a29f 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -161,13 +161,10 @@ private static boolean canUseDeArrowAPI() { } private static void handleDeArrowError(@NonNull String url) { - final long now = System.currentTimeMillis(); LogHelper.printDebug(() -> "Encountered DeArrow error. Url: " + url); - if (timeToResumeDeArrowAPICalls < now) { - timeToResumeDeArrowAPICalls = now + DEARROW_FAILURE_API_BACKOFF_MILLISECONDS; - if (SettingsEnum.ALT_THUMBNAILS_DEARROW_CONNECTION_TOAST.getBoolean()) { - ReVancedUtils.showToastLong(str("revanced_alt_thumbnail_dearrow_error_toast")); - } + timeToResumeDeArrowAPICalls = System.currentTimeMillis() + DEARROW_FAILURE_API_BACKOFF_MILLISECONDS; + if (SettingsEnum.ALT_THUMBNAILS_DEARROW_CONNECTION_TOAST.getBoolean()) { + ReVancedUtils.showToastLong(str("revanced_alt_thumbnail_dearrow_error_toast")); } } diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutDeArrowPreference.java b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutDeArrowPreference.java index f4e0ebf306..04e0ad194c 100644 --- a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutDeArrowPreference.java +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutDeArrowPreference.java @@ -13,7 +13,6 @@ public class AlternativeThumbnailsAboutDeArrowPreference extends Preference { private void init() { - setSelectable(true); setOnPreferenceClickListener(pref -> { Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse("https://dearrow.ajay.app")); From 8ae7aab27c36191089620726c1af62a50077478a Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:30:03 +0400 Subject: [PATCH 26/37] cleanup --- .../patches/AlternativeThumbnailsPatch.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 47af40a29f..523b13591b 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -437,7 +437,7 @@ static boolean verifyAltThumbnailExist(@NonNull String videoId, @NonNull Thumbna static void setAltThumbnailDoesNotExist(@NonNull String videoId, @NonNull ThumbnailQuality quality) { VerifiedQualities verified = getVerifiedQualities(videoId, false); - if (verified == null) return; + if (verified == null) return; // Entry was evicted after load but before this call. verified.setQualityVerified(videoId, quality, false); } @@ -445,20 +445,20 @@ static void setAltThumbnailDoesNotExist(@NonNull String videoId, @NonNull Thumbn * Highest quality verified as existing. */ @Nullable - ThumbnailQuality highestQualityVerified; + private ThumbnailQuality highestQualityVerified; /** * Lowest quality verified as not existing. */ @Nullable - ThumbnailQuality lowestQualityNotAvailable; + private ThumbnailQuality lowestQualityNotAvailable; /** * System time, of when to invalidate {@link #lowestQualityNotAvailable}. * Used only if fast mode is not enabled. */ - long timeToReVerifyLowestQuality; + private long timeToReVerifyLowestQuality; - synchronized void setQualityVerified(String videoId, ThumbnailQuality quality, boolean isVerified) { + private synchronized void setQualityVerified(String videoId, ThumbnailQuality quality, boolean isVerified) { if (isVerified) { if (highestQualityVerified == null || highestQualityVerified.ordinal() < quality.ordinal()) { highestQualityVerified = quality; @@ -572,8 +572,8 @@ static DecodedThumbnailUrl decodeImageUrl(String url) { /** User view tracking parameters, only present on some images. */ final String viewTrackingParameters; - private DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEndIndex, - int imageSizeStartIndex, int imageSizeEndIndex, int imageExtensionEndIndex) { + DecodedThumbnailUrl(String fullUrl, int videoIdStartIndex, int videoIdEndIndex, + int imageSizeStartIndex, int imageSizeEndIndex, int imageExtensionEndIndex) { originalFullUrl = fullUrl; sanitizedUrl = fullUrl.substring(0, imageExtensionEndIndex); urlPrefix = fullUrl.substring(0, videoIdStartIndex); From 9e4d13741e51cfafe473b06a24fbe608f625f76d Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:39:21 +0400 Subject: [PATCH 27/37] null check is not needed --- .../integrations/patches/AlternativeThumbnailsPatch.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 523b13591b..298051795e 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -437,7 +437,7 @@ static boolean verifyAltThumbnailExist(@NonNull String videoId, @NonNull Thumbna static void setAltThumbnailDoesNotExist(@NonNull String videoId, @NonNull ThumbnailQuality quality) { VerifiedQualities verified = getVerifiedQualities(videoId, false); - if (verified == null) return; // Entry was evicted after load but before this call. + //noinspection ConstantConditions verified.setQualityVerified(videoId, quality, false); } From a5bd27ddb9e41fd03651fe95a0b411c0e6fc81a0 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 8 Dec 2023 21:06:32 +0400 Subject: [PATCH 28/37] adjust setting name --- .../patches/AlternativeThumbnailsPatch.java | 22 +++++++++---------- .../integrations/settings/SettingsEnum.java | 14 ++++++------ ...AlternativeThumbnailsStatusPreference.java | 8 +++---- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 298051795e..835a649a7f 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -79,30 +79,30 @@ public final class AlternativeThumbnailsPatch { * Fix any bad imported data. */ private static Uri validateSettings() { - final int altThumbnailType = SettingsEnum.ALT_THUMBNAILS_STILLS_TIME.getInt(); + final int altThumbnailType = SettingsEnum.ALT_THUMBNAIL_STILLS_TIME.getInt(); if (altThumbnailType < 1 || altThumbnailType > 3) { ReVancedUtils.showToastLong("Invalid Alternative still thumbnail type: " + altThumbnailType + ". Using default"); - SettingsEnum.ALT_THUMBNAILS_STILLS_TIME.resetToDefault(); + SettingsEnum.ALT_THUMBNAIL_STILLS_TIME.resetToDefault(); } - Uri apiUri = Uri.parse(SettingsEnum.ALT_THUMBNAILS_DEARROW_API_URL.getString()); + Uri apiUri = Uri.parse(SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.getString()); // Cannot use unsecured 'http', otherwise the connections fail to start and no callbacks hooks are made. String scheme = apiUri.getScheme(); if (scheme == null || scheme.equals("http") || apiUri.getHost() == null) { ReVancedUtils.showToastLong("Invalid DeArrow API URL. Using default"); - SettingsEnum.ALT_THUMBNAILS_DEARROW_API_URL.resetToDefault(); + SettingsEnum.ALT_THUMBNAIL_DEARROW_API_URL.resetToDefault(); return validateSettings(); } return apiUri; } private static boolean usingDeArrow() { - return SettingsEnum.ALT_THUMBNAILS_DEARROW.getBoolean(); + return SettingsEnum.ALT_THUMBNAIL_DEARROW.getBoolean(); } private static boolean usingVideoStills() { - return SettingsEnum.ALT_THUMBNAILS_STILLS.getBoolean(); + return SettingsEnum.ALT_THUMBNAIL_STILLS.getBoolean(); } /** @@ -163,7 +163,7 @@ private static boolean canUseDeArrowAPI() { private static void handleDeArrowError(@NonNull String url) { LogHelper.printDebug(() -> "Encountered DeArrow error. Url: " + url); timeToResumeDeArrowAPICalls = System.currentTimeMillis() + DEARROW_FAILURE_API_BACKOFF_MILLISECONDS; - if (SettingsEnum.ALT_THUMBNAILS_DEARROW_CONNECTION_TOAST.getBoolean()) { + if (SettingsEnum.ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST.getBoolean()) { ReVancedUtils.showToastLong(str("revanced_alt_thumbnail_dearrow_error_toast")); } } @@ -350,7 +350,7 @@ static ThumbnailQuality getQualityToUse(@NonNull String originalSize) { return null; // Not a thumbnail for a regular video. } - final boolean useFastQuality = SettingsEnum.ALT_THUMBNAILS_STILLS_FAST.getBoolean(); + final boolean useFastQuality = SettingsEnum.ALT_THUMBNAIL_STILLS_FAST.getBoolean(); switch (quality) { case SDDEFAULT: // SD alt images have somewhat worse quality with washed out color and poor contrast. @@ -385,7 +385,7 @@ static ThumbnailQuality getQualityToUse(@NonNull String originalSize) { } String getAltImageNameToUse() { - return altImageName + SettingsEnum.ALT_THUMBNAILS_STILLS_TIME.getInt(); + return altImageName + SettingsEnum.ALT_THUMBNAIL_STILLS_TIME.getInt(); } } @@ -430,7 +430,7 @@ private static VerifiedQualities getVerifiedQualities(@NonNull String videoId, b static boolean verifyAltThumbnailExist(@NonNull String videoId, @NonNull ThumbnailQuality quality, @NonNull String imageUrl) { - VerifiedQualities verified = getVerifiedQualities(videoId, SettingsEnum.ALT_THUMBNAILS_STILLS_FAST.getBoolean()); + VerifiedQualities verified = getVerifiedQualities(videoId, SettingsEnum.ALT_THUMBNAIL_STILLS_FAST.getBoolean()); if (verified == null) return true; // Fast alt thumbnails is enabled. return verified.verifyYouTubeThumbnailExists(videoId, quality, imageUrl); } @@ -481,7 +481,7 @@ synchronized boolean verifyYouTubeThumbnailExists(@NonNull String videoId, @NonN return true; // Previously verified as existing. } - final boolean fastQuality = SettingsEnum.ALT_THUMBNAILS_STILLS_FAST.getBoolean(); + final boolean fastQuality = SettingsEnum.ALT_THUMBNAIL_STILLS_FAST.getBoolean(); if (lowestQualityNotAvailable != null && lowestQualityNotAvailable.ordinal() <= quality.ordinal()) { if (fastQuality || System.currentTimeMillis() < timeToReVerifyLowestQuality) { return false; // Previously verified as not existing. diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index 4573426cd7..ad464cf21f 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -56,13 +56,13 @@ public enum SettingsEnum { HIDE_WEB_SEARCH_RESULTS("revanced_hide_web_search_results", BOOLEAN, TRUE), // Layout - ALT_THUMBNAILS_STILLS("revanced_alt_thumbnails_stills", BOOLEAN, FALSE), - ALT_THUMBNAILS_STILLS_TIME("revanced_alt_thumbnails_stills_time", INTEGER, 2, parents(ALT_THUMBNAILS_STILLS)), - ALT_THUMBNAILS_STILLS_FAST("revanced_alt_thumbnails_stills_fast", BOOLEAN, FALSE, parents(ALT_THUMBNAILS_STILLS)), - ALT_THUMBNAILS_DEARROW("revanced_alt_thumbnails_dearrow", BOOLEAN, false), - ALT_THUMBNAILS_DEARROW_API_URL("revanced_alt_thumbnails_dearrow_api_url", STRING, - "https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true, parents(ALT_THUMBNAILS_DEARROW)), - ALT_THUMBNAILS_DEARROW_CONNECTION_TOAST("revanced_alt_thumbnails_dearrow_connection_toast", BOOLEAN, TRUE, parents(ALT_THUMBNAILS_DEARROW)), + ALT_THUMBNAIL_STILLS("revanced_alt_thumbnail_stills", BOOLEAN, FALSE), + ALT_THUMBNAIL_STILLS_TIME("revanced_alt_thumbnail_stills_time", INTEGER, 2, parents(ALT_THUMBNAIL_STILLS)), + ALT_THUMBNAIL_STILLS_FAST("revanced_alt_thumbnail_stills_fast", BOOLEAN, FALSE, parents(ALT_THUMBNAIL_STILLS)), + ALT_THUMBNAIL_DEARROW("revanced_alt_thumbnail_dearrow", BOOLEAN, false), + ALT_THUMBNAIL_DEARROW_API_URL("revanced_alt_thumbnail_dearrow_api_url", STRING, + "https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true, parents(ALT_THUMBNAIL_DEARROW)), + ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST("revanced_alt_thumbnail_dearrow_connection_toast", BOOLEAN, TRUE, parents(ALT_THUMBNAIL_DEARROW)), CUSTOM_FILTER("revanced_custom_filter", BOOLEAN, FALSE), CUSTOM_FILTER_STRINGS("revanced_custom_filter_strings", STRING, "", true, parents(CUSTOM_FILTER)), DISABLE_FULLSCREEN_AMBIENT_MODE("revanced_disable_fullscreen_ambient_mode", BOOLEAN, TRUE, true), diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java index 7b03a395fb..f74f739bf7 100644 --- a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java @@ -20,8 +20,8 @@ public class AlternativeThumbnailsStatusPreference extends Preference { private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> { - if (str.equals(SettingsEnum.ALT_THUMBNAILS_DEARROW.path) - || str.equals(SettingsEnum.ALT_THUMBNAILS_STILLS.path)) { + if (str.equals(SettingsEnum.ALT_THUMBNAIL_DEARROW.path) + || str.equals(SettingsEnum.ALT_THUMBNAIL_STILLS.path)) { // Because this listener may run before the ReVanced settings fragment updates SettingsEnum, // this could show the prior config and not the current. // @@ -67,8 +67,8 @@ protected void onPrepareForRemoval() { private void updateUI() { LogHelper.printDebug(() -> "updateUI"); - final boolean usingVideoStills = SettingsEnum.ALT_THUMBNAILS_STILLS.getBoolean(); - final boolean usingDeArrow = SettingsEnum.ALT_THUMBNAILS_DEARROW.getBoolean(); + final boolean usingVideoStills = SettingsEnum.ALT_THUMBNAIL_STILLS.getBoolean(); + final boolean usingDeArrow = SettingsEnum.ALT_THUMBNAIL_DEARROW.getBoolean(); final String summaryTextKey; if (usingDeArrow && usingVideoStills) { From 9b7a663dc7e49684caa31e528b980aba4fd9e27f Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 8 Dec 2023 21:18:21 +0400 Subject: [PATCH 29/37] more complex check is needed --- .../patches/AlternativeThumbnailsPatch.java | 9 ++++++--- .../AlternativeThumbnailsStatusPreference.java | 15 ++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 835a649a7f..d5c1bda04e 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -162,9 +162,12 @@ private static boolean canUseDeArrowAPI() { private static void handleDeArrowError(@NonNull String url) { LogHelper.printDebug(() -> "Encountered DeArrow error. Url: " + url); - timeToResumeDeArrowAPICalls = System.currentTimeMillis() + DEARROW_FAILURE_API_BACKOFF_MILLISECONDS; - if (SettingsEnum.ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST.getBoolean()) { - ReVancedUtils.showToastLong(str("revanced_alt_thumbnail_dearrow_error_toast")); + final long now = System.currentTimeMillis(); + if (timeToResumeDeArrowAPICalls < now) { + timeToResumeDeArrowAPICalls = now + DEARROW_FAILURE_API_BACKOFF_MILLISECONDS; + if (SettingsEnum.ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST.getBoolean()) { + ReVancedUtils.showToastLong(str("revanced_alt_thumbnail_dearrow_error_toast")); + } } } diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java index f74f739bf7..95b85770f7 100644 --- a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java @@ -20,15 +20,12 @@ public class AlternativeThumbnailsStatusPreference extends Preference { private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> { - if (str.equals(SettingsEnum.ALT_THUMBNAIL_DEARROW.path) - || str.equals(SettingsEnum.ALT_THUMBNAIL_STILLS.path)) { - // 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 complete and then the settings are up to date. - ReVancedUtils.runOnMainThread(this::updateUI); - } + // 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 complete and then the settings are up to date. + ReVancedUtils.runOnMainThread(this::updateUI); }; public AlternativeThumbnailsStatusPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { From b988a87efa0bca4ab7fb049c774315dc0fadf24e Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 8 Dec 2023 21:22:23 +0400 Subject: [PATCH 30/37] fixing strings --- .../AlternativeThumbnailsStatusPreference.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java index 95b85770f7..9e4c958f20 100644 --- a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java @@ -69,13 +69,13 @@ private void updateUI() { final String summaryTextKey; if (usingDeArrow && usingVideoStills) { - summaryTextKey = "revanced_alt_thumbnails_about_status_dearrow_stills"; + summaryTextKey = "revanced_alt_thumbnail_about_status_dearrow_stills"; } else if (usingDeArrow) { - summaryTextKey = "revanced_alt_thumbnails_about_status_dearrow"; + summaryTextKey = "revanced_alt_thumbnail_about_status_dearrow"; } else if (usingVideoStills) { - summaryTextKey = "revanced_alt_thumbnails_about_status_stills"; + summaryTextKey = "revanced_alt_thumbnail_about_status_stills"; } else { - summaryTextKey = "revanced_alt_thumbnails_about_status_disabled"; + summaryTextKey = "revanced_alt_thumbnail_about_status_disabled"; } setSummary(str(summaryTextKey)); From bbe423b881eabdfcd3cb272cc9cdcf367f942f7b Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 8 Dec 2023 21:33:35 +0400 Subject: [PATCH 31/37] logging, comments --- .../integrations/patches/AlternativeThumbnailsPatch.java | 1 + .../settingsmenu/AlternativeThumbnailsStatusPreference.java | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index d5c1bda04e..41ecdad08f 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -52,6 +52,7 @@ public final class AlternativeThumbnailsPatch { private static final Uri dearrowApiUri; + /** * The scheme and host of {@link #dearrowApiUri}. */ diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java index 9e4c958f20..02017ad172 100644 --- a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java @@ -24,7 +24,7 @@ public class AlternativeThumbnailsStatusPreference extends Preference { // 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 complete and then the settings are up to date. + // so all other listeners are done and SettingsEnum is up to date. ReVancedUtils.runOnMainThread(this::updateUI); }; @@ -42,10 +42,12 @@ public AlternativeThumbnailsStatusPreference(Context context) { } private void addChangeListener() { + LogHelper.printDebug(() -> "addChangeListener"); SharedPrefCategory.YOUTUBE.preferences.registerOnSharedPreferenceChangeListener(listener); } private void removeChangeListener() { + LogHelper.printDebug(() -> "removeChangeListener"); SharedPrefCategory.YOUTUBE.preferences.unregisterOnSharedPreferenceChangeListener(listener); } From 3b8a4866fe78c8caf9b4cd15828efa0ed1055510 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 8 Dec 2023 21:34:46 +0400 Subject: [PATCH 32/37] use new method --- .../patches/announcements/AnnouncementsPatch.java | 4 ++-- .../integrations/patches/components/LithoFilterPatch.java | 2 +- .../patches/playback/speed/CustomPlaybackSpeedPatch.java | 2 +- .../integrations/patches/theme/SeekbarColorPatch.java | 2 +- .../app/revanced/integrations/settings/SettingsEnum.java | 4 ++-- .../settingsmenu/SponsorBlockSettingsFragment.java | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/announcements/AnnouncementsPatch.java b/app/src/main/java/app/revanced/integrations/patches/announcements/AnnouncementsPatch.java index e68d10c174..0ae1ced63e 100644 --- a/app/src/main/java/app/revanced/integrations/patches/announcements/AnnouncementsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/announcements/AnnouncementsPatch.java @@ -43,7 +43,7 @@ public static void showAnnouncement(final Activity context) { if (connection.getResponseCode() != 200) { if (SettingsEnum.ANNOUNCEMENT_LAST_HASH.getString().isEmpty()) return; - SettingsEnum.ANNOUNCEMENT_LAST_HASH.saveValue(""); + SettingsEnum.ANNOUNCEMENT_LAST_HASH.resetToDefault(); ReVancedUtils.showToastLong("Failed to get announcement"); return; @@ -118,7 +118,7 @@ public static void showAnnouncement(final Activity context) { */ private static boolean emptyLastAnnouncementHash() { if (SettingsEnum.ANNOUNCEMENT_LAST_HASH.getString().isEmpty()) return true; - SettingsEnum.ANNOUNCEMENT_LAST_HASH.saveValue(""); + SettingsEnum.ANNOUNCEMENT_LAST_HASH.resetToDefault(); return false; } diff --git a/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java b/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java index 8712a55124..96b0976e54 100644 --- a/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/components/LithoFilterPatch.java @@ -133,7 +133,7 @@ private static String[] getFilterPatterns(SettingsEnum setting) { for (String pattern : patterns) { if (!StringTrieSearch.isValidPattern(pattern)) { ReVancedUtils.showToastLong("Invalid custom filter, resetting to default"); - setting.saveValue(setting.defaultValue); + setting.resetToDefault(); return getFilterPatterns(setting); } } diff --git a/app/src/main/java/app/revanced/integrations/patches/playback/speed/CustomPlaybackSpeedPatch.java b/app/src/main/java/app/revanced/integrations/patches/playback/speed/CustomPlaybackSpeedPatch.java index 3c2817223f..12e962d0b3 100644 --- a/app/src/main/java/app/revanced/integrations/patches/playback/speed/CustomPlaybackSpeedPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/playback/speed/CustomPlaybackSpeedPatch.java @@ -43,7 +43,7 @@ public class CustomPlaybackSpeedPatch { private static void resetCustomSpeeds(@NonNull String toastMessage) { ReVancedUtils.showToastLong(toastMessage); - SettingsEnum.CUSTOM_PLAYBACK_SPEEDS.saveValue(SettingsEnum.CUSTOM_PLAYBACK_SPEEDS.defaultValue); + SettingsEnum.CUSTOM_PLAYBACK_SPEEDS.resetToDefault(); } private static void loadCustomSpeeds() { diff --git a/app/src/main/java/app/revanced/integrations/patches/theme/SeekbarColorPatch.java b/app/src/main/java/app/revanced/integrations/patches/theme/SeekbarColorPatch.java index 942ff3c66a..41fa877d2c 100644 --- a/app/src/main/java/app/revanced/integrations/patches/theme/SeekbarColorPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/theme/SeekbarColorPatch.java @@ -48,7 +48,7 @@ private static void loadCustomSeekbarColor() { Color.colorToHSV(seekbarColor, customSeekbarColorHSV); } catch (Exception ex) { ReVancedUtils.showToastShort("Invalid seekbar color value. Using default value."); - SettingsEnum.SEEKBAR_CUSTOM_COLOR_VALUE.saveValue(SettingsEnum.SEEKBAR_CUSTOM_COLOR_VALUE.defaultValue); + SettingsEnum.SEEKBAR_CUSTOM_COLOR_VALUE.resetToDefault(); loadCustomSeekbarColor(); } } diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index ad464cf21f..2b18ab7841 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -434,7 +434,7 @@ private static void migrateOldSettingToNew(SettingsEnum oldSetting, SettingsEnum LogHelper.printInfo(() -> "Migrating old setting of '" + oldSetting.value + "' from: " + oldSetting + " into replacement setting: " + newSetting); newSetting.saveValue(oldSetting.value); - oldSetting.saveValue(oldSetting.defaultValue); // reset old value + oldSetting.resetToDefault(); } } @@ -705,7 +705,7 @@ public static boolean importJSON(@NonNull String settingsJsonString) { } else if (setting.includeWithImportExport() && !setting.isSetToDefault()) { LogHelper.printDebug(() -> "Resetting to default: " + setting); rebootSettingChanged |= setting.rebootApp; - setting.saveValue(setting.defaultValue); + setting.resetToDefault(); } } numberOfSettingsImported += SponsorBlockSettings.importCategoriesFromFlatJson(json); diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/SponsorBlockSettingsFragment.java b/app/src/main/java/app/revanced/integrations/settingsmenu/SponsorBlockSettingsFragment.java index 456bcdc8a9..e25ae7a5e9 100644 --- a/app/src/main/java/app/revanced/integrations/settingsmenu/SponsorBlockSettingsFragment.java +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/SponsorBlockSettingsFragment.java @@ -351,7 +351,7 @@ private void addGeneralCategory(final Context context, PreferenceScreen screen) DialogInterface.OnClickListener urlChangeListener = (dialog, buttonPressed) -> { if (buttonPressed == DialogInterface.BUTTON_NEUTRAL) { - SettingsEnum.SB_API_URL.saveValue(SettingsEnum.SB_API_URL.defaultValue); + SettingsEnum.SB_API_URL.resetToDefault(); ReVancedUtils.showToastLong(str("sb_api_url_reset")); } else if (buttonPressed == DialogInterface.BUTTON_POSITIVE) { String serverAddress = editText.getText().toString(); @@ -583,8 +583,8 @@ private void addLocalUserStats() { new AlertDialog.Builder(preference1.getContext()) .setTitle(str("sb_stats_self_saved_reset_title")) .setPositiveButton(android.R.string.yes, (dialog, whichButton) -> { - SettingsEnum.SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS.saveValue(SettingsEnum.SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS.defaultValue); - SettingsEnum.SB_LOCAL_TIME_SAVED_MILLISECONDS.saveValue(SettingsEnum.SB_LOCAL_TIME_SAVED_MILLISECONDS.defaultValue); + SettingsEnum.SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS.resetToDefault(); + SettingsEnum.SB_LOCAL_TIME_SAVED_MILLISECONDS.resetToDefault(); updateStatsSelfSaved.run(); }) .setNegativeButton(android.R.string.no, null).show(); From b7190176db0c9cba8ade5890929c30f6be4d90cf Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Sat, 9 Dec 2023 13:48:43 +0400 Subject: [PATCH 33/37] Include status code with the toast --- .../patches/AlternativeThumbnailsPatch.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index 41ecdad08f..f918c9fc8f 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -161,13 +161,16 @@ private static boolean canUseDeArrowAPI() { return false; } - private static void handleDeArrowError(@NonNull String url) { + private static void handleDeArrowError(@NonNull String url, int responseCode) { LogHelper.printDebug(() -> "Encountered DeArrow error. Url: " + url); final long now = System.currentTimeMillis(); if (timeToResumeDeArrowAPICalls < now) { timeToResumeDeArrowAPICalls = now + DEARROW_FAILURE_API_BACKOFF_MILLISECONDS; if (SettingsEnum.ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST.getBoolean()) { - ReVancedUtils.showToastLong(str("revanced_alt_thumbnail_dearrow_error_toast")); + String toastMessage = (responseCode != 0) + ? str("revanced_alt_thumbnail_dearrow_error", responseCode) + : str("revanced_alt_thumbnail_dearrow_error_generic"); + ReVancedUtils.showToastLong(toastMessage); } } } @@ -239,7 +242,7 @@ public static void handleCronetSuccess(UrlRequest request, @NonNull UrlResponseI if (usingDeArrow() && urlIsDeArrow(url)) { LogHelper.printDebug(() -> "handleCronetSuccess, responseCode: " + responseCode); - handleDeArrowError(url); + handleDeArrowError(url, responseCode); return; } @@ -292,7 +295,7 @@ public static void handleCronetFailure(UrlRequest request, String url = ((CronetUrlRequest) request).getHookedUrl(); if (urlIsDeArrow(url)) { LogHelper.printDebug(() -> "handleCronetFailure, exception: " + exception); - handleDeArrowError(url); + handleDeArrowError(url, 0); } } } catch (Exception ex) { From fc56d52ce691bab342459c86d702442983fa3f99 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Sat, 9 Dec 2023 13:55:54 +0400 Subject: [PATCH 34/37] cleanup --- .../patches/AlternativeThumbnailsPatch.java | 31 ++++++++++--------- ...AlternativeThumbnailsStatusPreference.java | 2 +- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index f918c9fc8f..a604de7ace 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -36,7 +36,7 @@ *

* Has an additional option to use 'fast' video still thumbnails, * where it forces sd thumbnail quality and skips verifying if the alt thumbnail image exists. - * The UI loading time will be the same or better than using the the original thumbnails, + * The UI loading time will be the same or better than using original thumbnails, * but thumbnails will initially fail to load for all live streams, unreleased, and occasionally very old videos. * If a failed thumbnail load is reloaded (ie: scroll off, then on screen), then the original thumbnail * is reloaded instead. Fast thumbnails requires using SD or lower thumbnail resolution, @@ -47,8 +47,8 @@ * such as videos subscription feed, watch history, or in search results. * - Save to a temporary file the video id's verified to have alt thumbnails. * This would speed up loading the watch history and users saved playlists. - * @noinspection unused */ +@SuppressWarnings("unused") public final class AlternativeThumbnailsPatch { private static final Uri dearrowApiUri; @@ -161,14 +161,14 @@ private static boolean canUseDeArrowAPI() { return false; } - private static void handleDeArrowError(@NonNull String url, int responseCode) { + private static void handleDeArrowError(@NonNull String url, int statusCode) { LogHelper.printDebug(() -> "Encountered DeArrow error. Url: " + url); final long now = System.currentTimeMillis(); if (timeToResumeDeArrowAPICalls < now) { timeToResumeDeArrowAPICalls = now + DEARROW_FAILURE_API_BACKOFF_MILLISECONDS; if (SettingsEnum.ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST.getBoolean()) { - String toastMessage = (responseCode != 0) - ? str("revanced_alt_thumbnail_dearrow_error", responseCode) + String toastMessage = (statusCode != 0) + ? str("revanced_alt_thumbnail_dearrow_error", statusCode) : str("revanced_alt_thumbnail_dearrow_error_generic"); ReVancedUtils.showToastLong(toastMessage); } @@ -182,9 +182,9 @@ private static void handleDeArrowError(@NonNull String url, int responseCode) { */ public static String overrideImageURL(String originalUrl) { try { - final boolean usingVideoStills = usingVideoStills(); final boolean usingDeArrow = usingDeArrow(); - if (!usingVideoStills && !usingDeArrow) { + final boolean usingVideoStills = usingVideoStills(); + if (!usingDeArrow && !usingVideoStills) { return originalUrl; } @@ -236,17 +236,17 @@ public static String overrideImageURL(String originalUrl) { */ public static void handleCronetSuccess(UrlRequest request, @NonNull UrlResponseInfo responseInfo) { try { - final int responseCode = responseInfo.getHttpStatusCode(); - if (responseCode != 200) { + final int statusCode = responseInfo.getHttpStatusCode(); + if (statusCode != 200) { String url = responseInfo.getUrl(); if (usingDeArrow() && urlIsDeArrow(url)) { - LogHelper.printDebug(() -> "handleCronetSuccess, responseCode: " + responseCode); - handleDeArrowError(url, responseCode); + LogHelper.printDebug(() -> "handleCronetSuccess, statusCode: " + statusCode); + handleDeArrowError(url, statusCode); return; } - if (usingVideoStills() && responseCode == 404) { + if (usingVideoStills() && statusCode == 404) { // Fast alt thumbnails is enabled and the thumbnail is not available. // The video is: // - live stream @@ -263,7 +263,7 @@ public static void handleCronetSuccess(UrlRequest request, @NonNull UrlResponseI ThumbnailQuality quality = ThumbnailQuality.altImageNameToQuality(decodedUrl.imageQuality); if (quality == null) { - // Video is a short or unknown quality, but somehow did not load. Should not happen. + // Video is a short or a seekbar thumbnail, but somehow did not load. Should not happen. LogHelper.printDebug(() -> "Failed to recognize image quality of url: " + decodedUrl.sanitizedUrl); return; } @@ -295,7 +295,10 @@ public static void handleCronetFailure(UrlRequest request, String url = ((CronetUrlRequest) request).getHookedUrl(); if (urlIsDeArrow(url)) { LogHelper.printDebug(() -> "handleCronetFailure, exception: " + exception); - handleDeArrowError(url, 0); + final int statusCode = (responseInfo != null) + ? responseInfo.getHttpStatusCode() + : 0; + handleDeArrowError(url, statusCode); } } } catch (Exception ex) { diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java index 02017ad172..97dc6d532a 100644 --- a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsStatusPreference.java @@ -66,8 +66,8 @@ protected void onPrepareForRemoval() { private void updateUI() { LogHelper.printDebug(() -> "updateUI"); - final boolean usingVideoStills = SettingsEnum.ALT_THUMBNAIL_STILLS.getBoolean(); final boolean usingDeArrow = SettingsEnum.ALT_THUMBNAIL_DEARROW.getBoolean(); + final boolean usingVideoStills = SettingsEnum.ALT_THUMBNAIL_STILLS.getBoolean(); final String summaryTextKey; if (usingDeArrow && usingVideoStills) { From 20fc977b66279805d09a0ecb6d73999a7a80a141 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Mon, 11 Dec 2023 00:06:40 +0100 Subject: [PATCH 35/37] refactor: Use initializer block --- .../AlternativeThumbnailsAboutDeArrowPreference.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutDeArrowPreference.java b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutDeArrowPreference.java index 04e0ad194c..353f40cc1e 100644 --- a/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutDeArrowPreference.java +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/AlternativeThumbnailsAboutDeArrowPreference.java @@ -11,8 +11,7 @@ */ @SuppressWarnings("unused") public class AlternativeThumbnailsAboutDeArrowPreference extends Preference { - - private void init() { + { setOnPreferenceClickListener(pref -> { Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse("https://dearrow.ajay.app")); @@ -23,18 +22,14 @@ private void init() { public AlternativeThumbnailsAboutDeArrowPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - init(); } public AlternativeThumbnailsAboutDeArrowPreference(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - init(); } public AlternativeThumbnailsAboutDeArrowPreference(Context context, AttributeSet attrs) { super(context, attrs); - init(); } public AlternativeThumbnailsAboutDeArrowPreference(Context context) { super(context); - init(); } } From b839cda64ccad1d42588c12ccaf0c9817678400a Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Mon, 11 Dec 2023 00:42:48 +0100 Subject: [PATCH 36/37] chore: Remove unnecessary query parameter --- .../patches/AlternativeThumbnailsPatch.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index a604de7ace..e0cb4c4dfd 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -1,13 +1,12 @@ package app.revanced.integrations.patches; -import static app.revanced.integrations.utils.StringRef.str; - import android.net.Uri; - import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - +import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.utils.LogHelper; +import app.revanced.integrations.utils.ReVancedUtils; import org.chromium.net.UrlRequest; import org.chromium.net.UrlResponseInfo; import org.chromium.net.impl.CronetUrlRequest; @@ -20,9 +19,7 @@ import java.util.Map; import java.util.concurrent.ExecutionException; -import app.revanced.integrations.settings.SettingsEnum; -import app.revanced.integrations.utils.LogHelper; -import app.revanced.integrations.utils.ReVancedUtils; +import static app.revanced.integrations.utils.StringRef.str; /** * Alternative YouTube thumbnails. @@ -136,7 +133,6 @@ private static String buildDeArrowThumbnailURL(String videoId, String fallbackUr return dearrowApiUri .buildUpon() .appendQueryParameter("videoID", videoId) - .appendQueryParameter("officialTime", "true") .appendQueryParameter("redirectUrl", fallbackUrl) .build() .toString(); From 9e3fa470e136daada239b444d8b8ed830c83f6b6 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Mon, 11 Dec 2023 00:44:21 +0100 Subject: [PATCH 37/37] chore: Fix typos --- .../patches/AlternativeThumbnailsPatch.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java index e0cb4c4dfd..49d8410301 100644 --- a/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/AlternativeThumbnailsPatch.java @@ -27,7 +27,7 @@ * Can show YouTube provided screen captures of beginning/middle/end of the video. * (ie: sd1.jpg, sd2.jpg, sd3.jpg). *

- * Or can show crowd sourced thumbnails provided by DeArrow (http://dearrow.ajay.app). + * Or can show crowdsourced thumbnails provided by DeArrow (...). *

* Or can use DeArrow and fall back to screen captures if DeArrow is not available. *

@@ -37,7 +37,7 @@ * but thumbnails will initially fail to load for all live streams, unreleased, and occasionally very old videos. * If a failed thumbnail load is reloaded (ie: scroll off, then on screen), then the original thumbnail * is reloaded instead. Fast thumbnails requires using SD or lower thumbnail resolution, - * because a noticeable number of videos do not have hq720 and too many fail to load. + * because a noticeable number of videos do not have hq720 and too much fail to load. *

* Ideas for improvements: * - Selectively allow using original thumbnails in some situations, @@ -143,7 +143,7 @@ private static boolean urlIsDeArrow(@NonNull String imageUrl) { } /** - * @return If this client has not recently experience any DeArrow API errors. + * @return If this client has not recently experienced any DeArrow API errors. */ private static boolean canUseDeArrowAPI() { if (timeToResumeDeArrowAPICalls == 0) { @@ -274,11 +274,11 @@ public static void handleCronetSuccess(UrlRequest request, @NonNull UrlResponseI /** * Injection point. - * + *

* To test failure cases, try changing the API URL to each of: - * - A non existent domain. + * - A non-existent domain. * - A url path of something incorrect (ie: /v1/nonExistentEndPoint). - * + *

* Known limitation: YT uses an infinite timeout, so this hook is never called if a host never responds. * But this does not appear to be a problem, as the DeArrow API has not been observed to 'go silent' * Instead if there's a problem it returns an error code status response, which is handled in this patch. @@ -590,6 +590,7 @@ static DecodedThumbnailUrl decodeImageUrl(String url) { ? "" : fullUrl.substring(imageExtensionEndIndex); } + /** @noinspection SameParameterValue*/ String createStillsUrl(@NonNull ThumbnailQuality qualityToUse, boolean includeViewTracking) { // Images could be upgraded to webp if they are not already, but this fails quite often, // especially for new videos uploaded in the last hour.