diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java index 0f976ea10..feeb379a7 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java @@ -588,7 +588,13 @@ public class Utils { showToast(messageToToast, Toast.LENGTH_LONG); } - private static void showToast(String messageToToast, int toastDuration) { + /** + * Safe to call from any thread. + * + * @param messageToToast Message to show. + * @param toastDuration Either {@link Toast#LENGTH_SHORT} or {@link Toast#LENGTH_LONG}. + */ + public static void showToast(String messageToToast, int toastDuration) { Objects.requireNonNull(messageToToast); runOnMainThreadNowOrLater(() -> { Context currentContext = context; diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/returnyoutubedislike/ReturnYouTubeDislike.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/returnyoutubedislike/ReturnYouTubeDislike.java index ade422260..355d2a7ee 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/returnyoutubedislike/ReturnYouTubeDislike.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/returnyoutubedislike/ReturnYouTubeDislike.java @@ -21,6 +21,7 @@ import android.text.Spanned; import android.text.style.ForegroundColorSpan; import android.text.style.ImageSpan; import android.text.style.ReplacementSpan; +import android.widget.Toast; import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; @@ -507,6 +508,11 @@ public class ReturnYouTubeDislike { try { RYDVoteData votingData = getFetchData(MAX_MILLISECONDS_TO_BLOCK_UI_WAITING_FOR_FETCH); if (votingData == null) { + // Method automatically prevents showing multiple toasts if the connection failed. + // This call is needed here in case the api call did succeed but took too long. + ReturnYouTubeDislikeApi.handleConnectionError( + str("revanced_ryd_failure_connection_timeout"), + null, null, Toast.LENGTH_SHORT); Logger.printDebug(() -> "Cannot add dislike to UI (RYD data not available)"); return original; } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java index 1ebeea223..18d46282c 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java @@ -4,8 +4,8 @@ import static app.revanced.extension.shared.StringRef.str; import static app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeRoutes.getRYDConnectionFromRoute; import android.util.Base64; +import android.widget.Toast; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.json.JSONException; @@ -30,15 +30,15 @@ import app.revanced.extension.youtube.settings.Settings; public class ReturnYouTubeDislikeApi { /** - * {@link #fetchVotes(String)} TCP connection timeout + * {@link #fetchVotes(String)} TCP connection timeout. */ - private static final int API_GET_VOTES_TCP_TIMEOUT_MILLISECONDS = 2 * 1000; // 2 Seconds. + private static final int API_GET_VOTES_TCP_TIMEOUT_MILLISECONDS = 3 * 1000; // 3 Seconds. /** * {@link #fetchVotes(String)} HTTP read timeout. * To locally debug and force timeouts, change this to a very small number (ie: 100) */ - private static final int API_GET_VOTES_HTTP_TIMEOUT_MILLISECONDS = 4 * 1000; // 4 Seconds. + private static final int API_GET_VOTES_HTTP_TIMEOUT_MILLISECONDS = 7 * 1000; // 7 Seconds. /** * Default connection and response timeout for voting and registration. @@ -53,6 +53,15 @@ public class ReturnYouTubeDislikeApi { */ private static final int HTTP_STATUS_CODE_SUCCESS = 200; + /** + * RYD API sometimes returns 401 (authorization error), even though the user id is valid. + * There is no known fix for this (resetting to a different user id does not fix it), + * so instead just quietly ignore the error. + * + * See RYD bug report. + */ + private static final int HTTP_STATUS_CODE_UNAUTHORIZED = 401; + /** * Indicates a client rate limit has been reached and the client must back off. */ @@ -232,14 +241,20 @@ public class ReturnYouTubeDislikeApi { } } - private static void handleConnectionError(@NonNull String toastMessage, - @Nullable Exception ex, - boolean showLongToast) { + /** + * @param toastDuration Either {@link Toast#LENGTH_SHORT} or {@link Toast#LENGTH_LONG}. + */ + public static void handleConnectionError(String toastMessage, + @Nullable Integer responseCode, + @Nullable Exception ex, + @Nullable Integer toastDuration) { if (!lastApiCallFailed && Settings.RYD_TOAST_ON_CONNECTION_ERROR.get()) { - if (showLongToast) { - Utils.showToastLong(toastMessage); - } else { - Utils.showToastShort(toastMessage); + if (responseCode != null && responseCode == HTTP_STATUS_CODE_UNAUTHORIZED) { + Logger.printInfo(() -> "Ignoring status code " + HTTP_STATUS_CODE_UNAUTHORIZED + + " (API authorization erorr)"); + return; // Do not set api failure field. + } else if (toastDuration != null) { + Utils.showToast(toastMessage, toastDuration); } } lastApiCallFailed = true; @@ -297,13 +312,13 @@ public class ReturnYouTubeDislikeApi { } else { // Unexpected response code. Most likely RYD is temporarily broken. handleConnectionError(str("revanced_ryd_failure_connection_status_code", responseCode), - null, true); + responseCode, null, Toast.LENGTH_LONG); } connection.disconnect(); // Something went wrong, might as well disconnect. } catch (SocketTimeoutException ex) { - handleConnectionError((str("revanced_ryd_failure_connection_timeout")), ex, false); + handleConnectionError((str("revanced_ryd_failure_connection_timeout")), null, ex, Toast.LENGTH_SHORT); } catch (IOException ex) { - handleConnectionError((str("revanced_ryd_failure_generic", ex.getMessage())), ex, true); + handleConnectionError((str("revanced_ryd_failure_generic", ex.getMessage())), null, ex, Toast.LENGTH_LONG); } catch (Exception ex) { // should never happen Logger.printException(() -> "fetchVotes failure", ex); @@ -344,13 +359,14 @@ public class ReturnYouTubeDislikeApi { String solution = solvePuzzle(challenge, difficulty); return confirmRegistration(userId, solution); } + handleConnectionError(str("revanced_ryd_failure_connection_status_code", responseCode), - null, true); + responseCode, null, Toast.LENGTH_LONG); connection.disconnect(); } catch (SocketTimeoutException ex) { - handleConnectionError(str("revanced_ryd_failure_connection_timeout"), ex, false); + handleConnectionError(str("revanced_ryd_failure_connection_timeout"), null, ex, Toast.LENGTH_SHORT); } catch (IOException ex) { - handleConnectionError(str("revanced_ryd_failure_generic", "registration failed"), ex, true); + handleConnectionError(str("revanced_ryd_failure_generic", "registration failed"), null, ex, Toast.LENGTH_LONG); } catch (Exception ex) { Logger.printException(() -> "Failed to register user", ex); // should never happen } @@ -393,12 +409,12 @@ public class ReturnYouTubeDislikeApi { Logger.printInfo(() -> "Failed to confirm registration for user: " + userId + " solution: " + solution + " responseCode: " + responseCode + " response: '" + response + "''"); handleConnectionError(str("revanced_ryd_failure_connection_status_code", responseCode), - null, true); + responseCode, null, Toast.LENGTH_LONG); } catch (SocketTimeoutException ex) { - handleConnectionError(str("revanced_ryd_failure_connection_timeout"), ex, false); + handleConnectionError(str("revanced_ryd_failure_connection_timeout"), null, ex, Toast.LENGTH_SHORT); } catch (IOException ex) { handleConnectionError(str("revanced_ryd_failure_generic", "confirm registration failed"), - ex, true); + null, ex, Toast.LENGTH_LONG); } catch (Exception ex) { Logger.printException(() -> "Failed to confirm registration for user: " + userId + "solution: " + solution, ex); @@ -469,12 +485,12 @@ public class ReturnYouTubeDislikeApi { Logger.printInfo(() -> "Failed to send vote for video: " + videoId + " vote: " + vote + " response code was: " + responseCode); handleConnectionError(str("revanced_ryd_failure_connection_status_code", responseCode), - null, true); + responseCode, null, Toast.LENGTH_LONG); connection.disconnect(); // something went wrong, might as well disconnect } catch (SocketTimeoutException ex) { - handleConnectionError(str("revanced_ryd_failure_connection_timeout"), ex, false); + handleConnectionError(str("revanced_ryd_failure_connection_timeout"), null, ex, Toast.LENGTH_SHORT); } catch (IOException ex) { - handleConnectionError(str("revanced_ryd_failure_generic", "send vote failed"), ex, true); + handleConnectionError(str("revanced_ryd_failure_generic", "send vote failed"), null, ex, Toast.LENGTH_LONG); } catch (Exception ex) { // should never happen Logger.printException(() -> "Failed to send vote for video: " + videoId + " vote: " + vote, ex); @@ -518,12 +534,12 @@ public class ReturnYouTubeDislikeApi { Logger.printInfo(() -> "Failed to confirm vote for video: " + videoId + " solution: " + solution + " responseCode: " + responseCode + " response: '" + response + "'"); handleConnectionError(str("revanced_ryd_failure_connection_status_code", responseCode), - null, true); + responseCode, null, Toast.LENGTH_LONG); } catch (SocketTimeoutException ex) { - handleConnectionError(str("revanced_ryd_failure_connection_timeout"), ex, false); + handleConnectionError(str("revanced_ryd_failure_connection_timeout"), null, ex, Toast.LENGTH_SHORT); } catch (IOException ex) { handleConnectionError(str("revanced_ryd_failure_generic", "confirm vote failed"), - ex, true); + null, ex, Toast.LENGTH_LONG); } catch (Exception ex) { Logger.printException(() -> "Failed to confirm vote for video: " + videoId + " solution: " + solution, ex); // should never happen