Skip to content

Commit ab352c4

Browse files
committed
Make RecordingDownloader more resillient against ReCaptchaExceptions
Implement a max number of requests per minute to prevent hitting reate limits and triggering ReCaptchaExceptions. This slows down the RecordingDownloader significantly and can be adjusted if needed. A request ist retried once when facing a ReCaptchaException.
1 parent 973d660 commit ab352c4

1 file changed

Lines changed: 50 additions & 0 deletions

File tree

extractor/src/test/java/org/schabi/newpipe/downloader/RecordingDownloader.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.nio.file.Files;
1616
import java.nio.file.Path;
1717
import java.nio.file.Paths;
18+
import java.util.Random;
1819

1920
import javax.annotation.Nonnull;
2021

@@ -46,6 +47,18 @@ class RecordingDownloader extends Downloader {
4647
private int index = 0;
4748
private final String path;
4849

50+
// try to prevent ReCaptchaExceptions / rate limits by tracking and throttling the requests
51+
/**
52+
* The maximum number of requests per minutes which are executed
53+
* by the {@link RecordingDownloader}.
54+
* The values can be adjusted when executing the downloader and running into problems.
55+
* <p>TODO: Allow adjusting the value by setting a param in the gradle command</p>
56+
*/
57+
private static final int MAX_REQUESTS_PER_MINUTE = 30;
58+
private static final long[] requestTimes = new long[MAX_REQUESTS_PER_MINUTE];
59+
private static int requestTimesCursor = -1;
60+
private static final Random throttleRandom = new Random();
61+
4962
/**
5063
* Creates the folder described by {@code stringPath} if it does not exists.
5164
* Deletes existing files starting with {@link RecordingDownloader#FILE_NAME_PREFIX}.
@@ -69,6 +82,43 @@ public RecordingDownloader(final String stringPath) throws IOException {
6982
@Override
7083
public Response execute(@Nonnull final Request request) throws IOException,
7184
ReCaptchaException {
85+
86+
// Delay the execution if the max number of requests per minute is reached
87+
long currentTime = System.currentTimeMillis();
88+
// the cursor points to the latest request time and the next position is the oldest one
89+
int oldestRequestTimeCursor = (requestTimesCursor + 1) % 12;
90+
long oldestRequestTime = requestTimes[oldestRequestTimeCursor];
91+
if (oldestRequestTime + 1000 >= currentTime) {
92+
try {
93+
Thread.sleep(throttleRandom.nextLong(currentTime - oldestRequestTime, 1000), 1500);
94+
} catch (InterruptedException e) {
95+
System.err.println("Error while throttling the RecordingDownloader.");
96+
e.printStackTrace();
97+
}
98+
}
99+
requestTimesCursor = oldestRequestTimeCursor; // the oldest value needs to be overridden
100+
requestTimes[requestTimesCursor] = System.currentTimeMillis();
101+
102+
// Handle ReCaptchaExceptions by retrying the request once after a while
103+
try {
104+
return executeRequest(request);
105+
} catch (ReCaptchaException e) {
106+
try {
107+
System.out.println("Throttling the RecordingDownloader to handle a ReCaptchaException. Sleeping for 35-60 seconds.");
108+
Thread.sleep(throttleRandom.nextInt(35_000, 60_000));
109+
} catch (InterruptedException ie) {
110+
System.err.println("Error while throttling the RecordingDownloader "
111+
+ "to handle a ReCaptchaException");
112+
ie.printStackTrace();
113+
e.printStackTrace();
114+
}
115+
return executeRequest(request);
116+
}
117+
}
118+
119+
@Nonnull
120+
private Response executeRequest(@Nonnull final Request request) throws IOException,
121+
ReCaptchaException {
72122
final Downloader downloader = DownloaderTestImpl.getInstance();
73123
Response response = downloader.execute(request);
74124
String cleanedResponseBody = response.responseBody().replaceAll(IP_V4_PATTERN, "127.0.0.1");

0 commit comments

Comments
 (0)