MarginaliaSearch/code/functions/live-capture/java/nu/marginalia/livecapture/BrowserlessClient.java
Viktor Lofgren 3dda8c228c (live-capture) Handle failed screenshot fetch in BrowserlessClient
Return an empty byte array when screenshot fetch fails, ensuring downstream processes are not impacted by null responses. Additionally, only attempt to upload the screenshot if the byte array is non-empty, preventing invalid data from being stored.
2024-09-27 14:52:05 +02:00

102 lines
3.2 KiB
Java

package nu.marginalia.livecapture;
import com.google.gson.Gson;
import nu.marginalia.model.gson.GsonFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Map;
/** Client for local browserless.io API */
public class BrowserlessClient implements AutoCloseable {
private static final Logger logger = LoggerFactory.getLogger(BrowserlessClient.class);
private final HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(30))
.build();
private final URI browserlessURI;
private final Gson gson = GsonFactory.get();
public BrowserlessClient(URI browserlessURI) {
this.browserlessURI = browserlessURI;
}
public String content(String url, GotoOptions gotoOptions) throws IOException, InterruptedException {
Map<String, Object> requestData = Map.of(
"url", url,
"gotoOptions", gotoOptions
);
var request = HttpRequest.newBuilder()
.uri(browserlessURI.resolve("/content"))
.method("POST", HttpRequest.BodyPublishers.ofString(
gson.toJson(requestData)
))
.header("Content-type", "application/json")
.build();
var rsp = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (rsp.statusCode() >= 300) {
logger.info("Failed to fetch content for {}, status {}", url, rsp.statusCode());
return null;
}
return rsp.body();
}
public byte[] screenshot(String url, GotoOptions gotoOptions, ScreenshotOptions screenshotOptions)
throws IOException, InterruptedException {
Map<String, Object> requestData = Map.of(
"url", url,
"options", screenshotOptions,
"gotoOptions", gotoOptions
);
var request = HttpRequest.newBuilder()
.uri(browserlessURI.resolve("/screenshot"))
.method("POST", HttpRequest.BodyPublishers.ofString(
gson.toJson(requestData)
))
.header("Content-type", "application/json")
.build();
var rsp = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (rsp.statusCode() >= 300) {
logger.info("Failed to fetch screenshot for {}, status {}", url, rsp.statusCode());
return new byte[0];
}
return rsp.body();
}
@Override
public void close() throws Exception {
httpClient.shutdownNow();
}
public record ScreenshotOptions(boolean fullPage, String type) {
public static ScreenshotOptions defaultValues() {
return new ScreenshotOptions(false, "png");
}
}
public record GotoOptions(String waitUntil, long timeout) {
public static GotoOptions defaultValues() {
return new GotoOptions("networkidle2", Duration.ofSeconds(10).toMillis());
}
}
}