From 5858b2661fa65cf7881574f8dce09e64915aa6fa Mon Sep 17 00:00:00 2001 From: jollySleeper Date: Fri, 26 Jun 2026 05:45:35 +0530 Subject: [PATCH] feat(feeds): add consecutive-failure backoff to FEED_REFRESH loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When YouTube rate-limits an instance (429, CAPTCHA, or bot-detection), the current FEED_REFRESH loop catches the error and immediately moves to the next channel. This means it can fire off dozens of failed requests in quick succession against a service that is actively rejecting it. Add a simple consecutive-failure counter: after 5 failures in a row (strongly suggesting rate-limiting rather than individual channel issues), pause for 5 minutes before resuming. Any successful refresh resets the counter. This is deliberately not exponential backoff — the loop already has built-in per-channel pacing, so a single fixed pause is sufficient to let rate-limit windows expire without adding complexity. Note: Piped's DownloaderImpl does not currently throw ReCaptchaException on 429 responses (the handler is commented out), so rate-limiting manifests as ParsingException or IOException when the extractor fails to parse YouTube's error page. A type-agnostic consecutive-failure counter handles this correctly regardless of the specific exception type. --- src/main/java/me/kavin/piped/Main.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/me/kavin/piped/Main.java b/src/main/java/me/kavin/piped/Main.java index 0de69657..849b0460 100644 --- a/src/main/java/me/kavin/piped/Main.java +++ b/src/main/java/me/kavin/piped/Main.java @@ -276,6 +276,8 @@ public void run() { System.out.println("FeedRefresh: " + channelIds.size() + " channels, one every " + delay + "ms"); + int consecutiveFailures = 0; + for (String channelId : channelIds) { try { var info = ChannelInfo.getInfo("https://youtube.com/channel/" + channelId); @@ -285,8 +287,16 @@ public void run() { Multithreading.runAsync(() -> ChannelHelpers.federateChannelInfo(info)); if (tabInfo != null) ChannelHelpers.updateChannelVideos(info, tabInfo); + consecutiveFailures = 0; } catch (Exception e) { + consecutiveFailures++; ExceptionHandler.handle(e); + if (consecutiveFailures >= 5) { + System.out.println("FeedRefresh: " + consecutiveFailures + + " consecutive failures, likely rate-limited — pausing 5 minutes"); + Thread.sleep(TimeUnit.MINUTES.toMillis(5)); + consecutiveFailures = 0; + } } Thread.sleep(delay); }