2024-09-27 11:45:54 +00:00
|
|
|
package nu.marginalia.livecapture;
|
|
|
|
|
|
|
|
import com.google.gson.Gson;
|
2025-02-04 12:36:49 +00:00
|
|
|
import nu.marginalia.WmsaHome;
|
2024-09-27 11:45:54 +00:00
|
|
|
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;
|
2025-02-04 13:05:36 +00:00
|
|
|
import java.util.Optional;
|
2024-09-27 11:45:54 +00:00
|
|
|
|
|
|
|
/** Client for local browserless.io API */
|
|
|
|
public class BrowserlessClient implements AutoCloseable {
|
2025-01-09 13:51:11 +00:00
|
|
|
|
2024-09-27 11:45:54 +00:00
|
|
|
private static final Logger logger = LoggerFactory.getLogger(BrowserlessClient.class);
|
2025-01-09 13:51:11 +00:00
|
|
|
private static final String BROWSERLESS_TOKEN = System.getProperty("live-capture.browserless-token", "BROWSERLESS_TOKEN");
|
2024-09-27 11:45:54 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2025-02-04 12:36:49 +00:00
|
|
|
private final String userAgent = WmsaHome.getUserAgent().uaString();
|
|
|
|
|
2024-09-27 11:45:54 +00:00
|
|
|
public BrowserlessClient(URI browserlessURI) {
|
|
|
|
this.browserlessURI = browserlessURI;
|
|
|
|
}
|
|
|
|
|
2025-02-04 13:05:36 +00:00
|
|
|
public Optional<String> content(String url, GotoOptions gotoOptions) throws IOException, InterruptedException {
|
2024-09-27 11:45:54 +00:00
|
|
|
Map<String, Object> requestData = Map.of(
|
|
|
|
"url", url,
|
2025-02-04 12:36:49 +00:00
|
|
|
"userAgent", userAgent,
|
2024-09-27 11:45:54 +00:00
|
|
|
"gotoOptions", gotoOptions
|
|
|
|
);
|
|
|
|
|
|
|
|
var request = HttpRequest.newBuilder()
|
2025-01-09 13:51:11 +00:00
|
|
|
.uri(browserlessURI.resolve("/content?token="+BROWSERLESS_TOKEN))
|
2024-09-27 11:45:54 +00:00
|
|
|
.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());
|
2025-02-04 13:05:36 +00:00
|
|
|
return Optional.empty();
|
2024-09-27 11:45:54 +00:00
|
|
|
}
|
|
|
|
|
2025-02-04 13:05:36 +00:00
|
|
|
return Optional.of(rsp.body());
|
2024-09-27 11:45:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public byte[] screenshot(String url, GotoOptions gotoOptions, ScreenshotOptions screenshotOptions)
|
|
|
|
throws IOException, InterruptedException {
|
|
|
|
|
|
|
|
Map<String, Object> requestData = Map.of(
|
|
|
|
"url", url,
|
2025-02-04 12:36:49 +00:00
|
|
|
"userAgent", userAgent,
|
2024-09-27 11:45:54 +00:00
|
|
|
"options", screenshotOptions,
|
|
|
|
"gotoOptions", gotoOptions
|
|
|
|
);
|
|
|
|
|
|
|
|
var request = HttpRequest.newBuilder()
|
2025-01-09 13:51:11 +00:00
|
|
|
.uri(browserlessURI.resolve("/screenshot?token="+BROWSERLESS_TOKEN))
|
2024-09-27 11:45:54 +00:00
|
|
|
.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());
|
2024-09-27 12:52:05 +00:00
|
|
|
return new byte[0];
|
2024-09-27 11:45:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rsp.body();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2025-02-04 13:05:36 +00:00
|
|
|
public void close() {
|
2024-09-27 11:45:54 +00:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|