From 1b2a8bad93a220be6aa44de89a82dcfdd8db0a59 Mon Sep 17 00:00:00 2001 From: kohlerpop1 Date: Mon, 19 Feb 2024 14:55:59 -0500 Subject: [PATCH] Converted from Optional to ActionResult Moved Logger creation to LoggerFactory Fixed creating more than 1 recording thread for each livestream And more optimizations! --- .../tiktok/TikTokLiveClientBuilder.java | 45 +----- .../tiktok/TikTokLiveHttpClient.java | 152 ++++++++---------- .../tiktok/common/ActionResult.java | 42 ++--- .../tiktok/common/LoggerFactory.java | 36 +++-- .../jwdeveloper/tiktok/http/HttpClient.java | 13 +- .../tiktok/http/HttpClientBuilder.java | 3 +- .../tiktok/http/HttpProxyClient.java | 18 +-- .../recorder/impl/RecorderListener.java | 45 +++--- .../recorder/impl/data/RecorderSettings.java | 2 + 9 files changed, 150 insertions(+), 206 deletions(-) diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java index 80c5793..e63f45e 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java @@ -44,14 +44,12 @@ import io.github.jwdeveloper.tiktok.mappers.*; import io.github.jwdeveloper.tiktok.mappers.data.MappingResult; import io.github.jwdeveloper.tiktok.mappers.handlers.*; import io.github.jwdeveloper.tiktok.messages.webcast.*; -import io.github.jwdeveloper.tiktok.utils.ConsoleColors; import io.github.jwdeveloper.tiktok.websocket.TikTokWebSocketClient; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import java.util.logging.Formatter; -import java.util.logging.*; +import java.util.logging.Logger; public class TikTokLiveClientBuilder implements LiveClientBuilder { @@ -61,8 +59,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { protected Consumer onCustomMappings; protected Logger logger; - public TikTokLiveClientBuilder(String userName) - { + public TikTokLiveClientBuilder(String userName) { this.clientSettings = LiveClientSettings.createDefault(); this.clientSettings.setHostName(userName); this.tikTokEventHandler = new TikTokLiveEventHandler(); @@ -75,7 +72,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { return this; } - public TikTokLiveClientBuilder configure(Consumer onConfigure) { onConfigure.accept(clientSettings); return this; @@ -88,44 +84,20 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { } protected void validate() { - if (clientSettings.getClientLanguage() == null || clientSettings.getClientLanguage().isEmpty()) { + if (clientSettings.getClientLanguage() == null || clientSettings.getClientLanguage().isEmpty()) clientSettings.setClientLanguage("en"); - } - - if (clientSettings.getHostName() == null || clientSettings.getHostName().isEmpty()) { + if (clientSettings.getHostName() == null || clientSettings.getHostName().isEmpty()) throw new TikTokLiveException("HostName can not be null"); - } - if (clientSettings.getHostName().startsWith("@")) { + if (clientSettings.getHostName().startsWith("@")) clientSettings.setHostName(clientSettings.getHostName().substring(1)); - } - var httpSettings = clientSettings.getHttpSettings(); httpSettings.getParams().put("app_language", clientSettings.getClientLanguage()); httpSettings.getParams().put("webcast_language", clientSettings.getClientLanguage()); - this.logger = LoggerFactory.create(clientSettings.getHostName(), clientSettings); - var handler = new ConsoleHandler(); - handler.setFormatter(new Formatter() { - @Override - public String format(LogRecord record) { - var sb = new StringBuilder(); - sb.append(ConsoleColors.GREEN).append("[").append(record.getLoggerName()).append("] "); - sb.append(ConsoleColors.GREEN).append("[").append(record.getLevel()).append("]: "); - sb.append(ConsoleColors.WHITE_BRIGHT).append(record.getMessage()); - sb.append(ConsoleColors.RESET).append("\n"); - return sb.toString(); - } - }); - logger.setUseParentHandlers(false); - logger.addHandler(handler); - logger.setLevel(clientSettings.getLogLevel()); - if (!clientSettings.isPrintToConsole()) { - logger.setLevel(Level.OFF); - } } public LiveClient build() { @@ -266,13 +238,11 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { return this; } - // @Override public LiveClientBuilder onChest(EventConsumer event) { tikTokEventHandler.subscribe(TikTokChestEvent.class, event); return this; } - public TikTokLiveClientBuilder onLinkMicFanTicket(EventConsumer event) { tikTokEventHandler.subscribe(TikTokLinkMicFanTicketEvent.class, event); return this; @@ -329,14 +299,12 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { return this; } - @Override public TikTokLiveClientBuilder onRoomInfo(EventConsumer event) { tikTokEventHandler.subscribe(TikTokRoomInfoEvent.class, event); return this; } - public TikTokLiveClientBuilder onLivePaused(EventConsumer event) { tikTokEventHandler.subscribe(TikTokLivePausedEvent.class, event); return this; @@ -363,7 +331,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { return this; } - public TikTokLiveClientBuilder onGift(EventConsumer event) { tikTokEventHandler.subscribe(TikTokGiftEvent.class, event); return this; @@ -374,7 +341,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { return this; } - public TikTokLiveClientBuilder onLinkMicArmies(EventConsumer event) { tikTokEventHandler.subscribe(TikTokLinkMicArmiesEvent.class, event); return this; @@ -451,7 +417,6 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { return this; } - public TikTokLiveClientBuilder onJoin(EventConsumer event) { tikTokEventHandler.subscribe(TikTokJoinEvent.class, event); return this; diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java index 6752bb2..7f22e04 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java @@ -23,7 +23,7 @@ package io.github.jwdeveloper.tiktok; import com.google.protobuf.InvalidProtocolBufferException; -import io.github.jwdeveloper.tiktok.common.LoggerFactory; +import io.github.jwdeveloper.tiktok.common.*; import io.github.jwdeveloper.tiktok.data.requests.*; import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; import io.github.jwdeveloper.tiktok.exceptions.*; @@ -32,7 +32,6 @@ import io.github.jwdeveloper.tiktok.http.mappers.*; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse; import java.net.http.HttpResponse; -import java.util.Optional; import java.util.logging.Logger; public class TikTokLiveHttpClient implements LiveHttpClient @@ -65,118 +64,98 @@ public class TikTokLiveHttpClient implements LiveHttpClient this(new HttpClientFactory(LiveClientSettings.createDefault()), LiveClientSettings.createDefault()); } - public GiftsData.Response fetchGiftsData() { - var url = TIKTOK_URL_WEBCAST + "gift/list/"; var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings(); if (proxyClientSettings.isEnabled()) { while (proxyClientSettings.hasNext()) { try { - var optional = httpFactory.client(url) - .build() - .toJsonResponse(); - - if (optional.isEmpty()) { - throw new TikTokLiveRequestException("Unable to fetch gifts information's"); - } - var json = optional.get(); - return giftsDataMapper.map(json); + return getGiftsData(); } catch (TikTokProxyRequestException ignored) {} } } - var optional = httpFactory.client(url) - .build() - .toJsonResponse(); + return getGiftsData(); + } - if (optional.isEmpty()) { - throw new TikTokLiveRequestException("Unable to fetch gifts information's"); - } + public GiftsData.Response getGiftsData() { + var url = TIKTOK_URL_WEBCAST + "gift/list/"; + var result = httpFactory.client(url) + .build() + .toJsonResponse(); - var json = optional.get(); + if (result.isFailure()) + throw new TikTokLiveRequestException("Unable to fetch gifts information's"+result.toStack()); + + var json = result.getContent(); return giftsDataMapper.map(json); } @Override public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) { - var url = TIKTOK_URL_WEB + "api-live/user/room"; var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings(); if (proxyClientSettings.isEnabled()) { while (proxyClientSettings.hasNext()) { try { - var optional = httpFactory.client(url) - .withParam("uniqueId", request.getUserName()) - .withParam("sourceType", "54") - .build() - .toJsonResponse(); - - if (optional.isEmpty()) { - throw new TikTokLiveRequestException("Unable to get information's about user"); - } - - var json = optional.get(); - return liveUserDataMapper.map(json); + return getLiveUserData(request); } catch (TikTokProxyRequestException ignored) {} } } - var optional = httpFactory.client(url) - .withParam("uniqueId", request.getUserName()) - .withParam("sourceType", "54") - .build() - .toJsonResponse(); + return getLiveUserData(request); + } - if (optional.isEmpty()) { - throw new TikTokLiveRequestException("Unable to get information's about user"); - } + public LiveUserData.Response getLiveUserData(LiveUserData.Request request) { + var url = TIKTOK_URL_WEB + "api-live/user/room"; + var result = httpFactory.client(url) + .withParam("uniqueId", request.getUserName()) + .withParam("sourceType", "54") + .build() + .toJsonResponse(); - var json = optional.get(); + if (result.isFailure()) + throw new TikTokLiveRequestException("Unable to get information's about user"+result.toStack()); + + var json = result.getContent(); return liveUserDataMapper.map(json); } @Override public LiveData.Response fetchLiveData(LiveData.Request request) { - var url = TIKTOK_URL_WEBCAST + "room/info"; var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings(); if (proxyClientSettings.isEnabled()) { while (proxyClientSettings.hasNext()) { try { - var optional = httpFactory.client(url) - .withParam("room_id", request.getRoomId()) - .build() - .toJsonResponse(); - - if (optional.isEmpty()) { - throw new TikTokLiveRequestException("Unable to get info about live room"); - } - - var json = optional.get(); - return liveDataMapper.map(json); + return getLiveData(request); } catch (TikTokProxyRequestException ignored) {} } } - var optional = httpFactory.client(url) - .withParam("room_id", request.getRoomId()) - .build() - .toJsonResponse(); + return getLiveData(request); + } - if (optional.isEmpty()) { - throw new TikTokLiveRequestException("Unable to get info about live room"); - } + public LiveData.Response getLiveData(LiveData.Request request) { + var url = TIKTOK_URL_WEBCAST + "room/info"; + var result = httpFactory.client(url) + .withParam("room_id", request.getRoomId()) + .build() + .toJsonResponse(); - var json = optional.get(); + if (result.isFailure()) + throw new TikTokLiveRequestException("Unable to get info about live room"+result.toStack()); + + var json = result.getContent(); return liveDataMapper.map(json); } @Override public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) { - HttpResponse credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> getStarterPayload(request.getRoomId())); + var result = getStartingPayload(request); + HttpResponse credentialsResponse = result.getContent(); // Always guaranteed to have response try { - var optionalHeader = credentialsResponse.headers().firstValue("x-set-tt-cookie"); - if (optionalHeader.isEmpty()) { - logger.warning("SignServer Headers: "+credentialsResponse.headers().map()); - throw new TikTokSignServerException("Sign server did not return the x-set-tt-cookie header"); + var resultHeader = ActionResult.of(credentialsResponse.headers().firstValue("x-set-tt-cookie")); + if (resultHeader.isFailure()) { + logger.warning("SignServer Headers: "+request.getRoomId()+" - "+credentialsResponse.headers().map()); + throw new TikTokSignServerException("Sign server did not return the x-set-tt-cookie header"+result.toStack()); } - var websocketCookie = optionalHeader.get(); + var websocketCookie = resultHeader.getContent(); var webcastResponse = WebcastResponse.parseFrom(credentialsResponse.body()); var webSocketUrl = httpFactory .client(webcastResponse.getPushServer()) @@ -190,11 +169,23 @@ public class TikTokLiveHttpClient implements LiveHttpClient return new LiveConnectionData.Response(websocketCookie, webSocketUrl, webcastResponse); } catch (InvalidProtocolBufferException e) { - throw new TikTokSignServerException("Unable to parse websocket credentials response to WebcastResponse"); + throw new TikTokSignServerException("Unable to parse websocket credentials response to WebcastResponse"+result.toStack()); } } - HttpResponse getStarterPayload(String room_id) { + private ActionResult> getStartingPayload(LiveConnectionData.Request request) { + var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings(); + if (proxyClientSettings.isEnabled()) { + while (proxyClientSettings.hasNext()) { + try { + return getByteResponse(request.getRoomId()); + } catch (TikTokProxyRequestException | TikTokSignServerException ignored) {} + } + } + return getByteResponse(request.getRoomId()); + } + + private ActionResult> getByteResponse(String room_id) { HttpClientBuilder builder = httpFactory.client(TIKTOK_SIGN_API) .withParam("client", "ttlive-java") .withParam("uuc", "1") @@ -203,24 +194,11 @@ public class TikTokLiveHttpClient implements LiveHttpClient if (clientSettings.getApiKey() != null) builder.withParam("apiKey", clientSettings.getApiKey()); - var optional = builder.build().toResponse(); + var result = builder.build().toResponse(); - if (optional.isEmpty()) { - throw new TikTokSignServerException("Unable to get websocket connection credentials"); - } - return optional.get(); - } + if (result.isFailure()) + throw new TikTokSignServerException("Unable to get websocket connection credentials"+result.toStack()); - Optional> getOptionalProxyResponse(LiveConnectionData.Request request) { - var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings(); - if (proxyClientSettings.isEnabled()) { - while (proxyClientSettings.hasNext()) { - try { - HttpResponse credentialsResponse = getStarterPayload(request.getRoomId()); - return Optional.of(credentialsResponse); - } catch (TikTokProxyRequestException | TikTokSignServerException ignored) {} - } - } - return Optional.empty(); + return result; } } \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/common/ActionResult.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/common/ActionResult.java index bf68260..34ab7b3 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/common/ActionResult.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/common/ActionResult.java @@ -2,11 +2,12 @@ package io.github.jwdeveloper.tiktok.common; import lombok.Data; +import java.util.Optional; import java.util.function.Function; @Data -public class ActionResult -{ +public class ActionResult { + private boolean success = true; private T content; private String message; @@ -29,6 +30,10 @@ public class ActionResult return new ActionResultBuilder<>(content); } + public static ActionResult of(Optional optional) { + return new ActionResult<>(optional.orElse(null), optional.isPresent()); + } + public boolean isFailure() { return !isSuccess(); } @@ -36,48 +41,47 @@ public class ActionResult public boolean hasMessage() { return message != null; } + public String toStack() { + return hasMessage() ? " - "+message : ""; + } public boolean hasContent() { return content != null; } - public static ActionResult success() { - return new ActionResult<>(null, true); - } - public ActionResult cast(Output output) { return new ActionResult<>(output, this.isSuccess(), this.getMessage()); } public ActionResult cast() { - return new ActionResult<>(null, this.isSuccess(), this.getMessage()); + return cast(null); } public ActionResult map(Function mapper) { return hasContent() ? cast(mapper.apply(content)) : cast(); } - public static ActionResult cast(ActionResult action, Output output) { - return new ActionResult<>(output, action.isSuccess(), action.getMessage()); - } - - public static ActionResult success(T payload) { - return new ActionResult<>(payload, true); - } - public static ActionResult success(T payload, String message) { return new ActionResult<>(payload, true, message); } - public static ActionResult failure() { - return new ActionResult<>(null, false); + public static ActionResult success(T payload) { + return success(payload, null); } - public static ActionResult failure(String message) { - return new ActionResult<>(null, false, message); + public static ActionResult success() { + return success(null); } public static ActionResult failure(T target, String message) { return new ActionResult<>(target, false, message); } + + public static ActionResult failure(String message) { + return failure(null, message); + } + + public static ActionResult failure() { + return failure(null); + } } \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/common/LoggerFactory.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/common/LoggerFactory.java index 2e3f61d..fcd2227 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/common/LoggerFactory.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/common/LoggerFactory.java @@ -9,23 +9,25 @@ public class LoggerFactory { public static Logger create(String name, LiveClientSettings settings) { Logger logger = Logger.getLogger(name); - var handler = new ConsoleHandler(); - handler.setFormatter(new Formatter() { - @Override - public String format(LogRecord record) { - var sb = new StringBuilder(); - sb.append(ConsoleColors.GREEN).append("[").append(record.getLoggerName()).append("] "); - sb.append(ConsoleColors.GREEN).append("[").append(record.getLevel()).append("]: "); - sb.append(ConsoleColors.WHITE_BRIGHT).append(record.getMessage()); - sb.append(ConsoleColors.RESET).append("\n"); - return sb.toString(); - } - }); - logger.setUseParentHandlers(false); - logger.addHandler(handler); - logger.setLevel(settings.getLogLevel()); - if (!settings.isPrintToConsole()) { - logger.setLevel(Level.OFF); + if (logger.getHandlers().length == 0) { + var handler = new ConsoleHandler(); + handler.setFormatter(new Formatter() + { + @Override + public String format(LogRecord record) { + var sb = new StringBuilder(); + sb.append(ConsoleColors.GREEN).append("[").append(record.getLoggerName()).append("] "); + sb.append(ConsoleColors.GREEN).append("[").append(record.getLevel()).append("]: "); + sb.append(ConsoleColors.WHITE_BRIGHT).append(record.getMessage()); + sb.append(ConsoleColors.RESET).append("\n"); + return sb.toString(); + } + }); + logger.setUseParentHandlers(false); + logger.addHandler(handler); + logger.setLevel(settings.getLogLevel()); + if (!settings.isPrintToConsole()) + logger.setLevel(Level.OFF); } return logger; } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpClient.java index 2716bdb..d31a17f 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpClient.java @@ -35,8 +35,8 @@ import java.util.regex.*; import java.util.stream.Collectors; @AllArgsConstructor -public class HttpClient -{ +public class HttpClient { + protected final HttpClientSettings httpClientSettings; protected final String url; private final Pattern pattern = Pattern.compile("charset=(.*?)(?=&|$)"); @@ -71,13 +71,8 @@ public class HttpClient } } - public Optional toBinaryResponse() { - var optional = toResponse(); - if (optional.isEmpty()) { - return Optional.empty(); - } - var body = optional.get().body(); - return Optional.of(body); + public ActionResult toBinaryResponse() { + return toResponse().map(HttpResponse::body); } public URI toUrl() { diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpClientBuilder.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpClientBuilder.java index fe0ce98..8a04f58 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpClientBuilder.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpClientBuilder.java @@ -78,7 +78,8 @@ public class HttpClientBuilder { } public HttpClient build() { - if (httpClientSettings.getProxyClientSettings().isEnabled()) + var proxyClientSettings = httpClientSettings.getProxyClientSettings(); + if (proxyClientSettings.isEnabled() && proxyClientSettings.hasNext()) return new HttpProxyClient(httpClientSettings, url); return new HttpClient(httpClientSettings, url); } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java index 6e3b75a..92d9ace 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java @@ -22,6 +22,7 @@ */ package io.github.jwdeveloper.tiktok.http; +import io.github.jwdeveloper.tiktok.common.ActionResult; import io.github.jwdeveloper.tiktok.data.settings.*; import io.github.jwdeveloper.tiktok.exceptions.*; @@ -35,8 +36,8 @@ import java.security.cert.X509Certificate; import java.util.*; import java.util.stream.Collectors; -public class HttpProxyClient extends HttpClient -{ +public class HttpProxyClient extends HttpClient { + private final ProxyClientSettings proxySettings; public HttpProxyClient(HttpClientSettings httpClientSettings, String url) { @@ -44,14 +45,14 @@ public class HttpProxyClient extends HttpClient this.proxySettings = httpClientSettings.getProxyClientSettings(); } - public Optional> toResponse() { + public ActionResult> toResponse() { return switch (proxySettings.getType()) { case HTTP, DIRECT -> handleHttpProxyRequest(); default -> handleSocksProxyRequest(); }; } - public Optional> handleHttpProxyRequest() { + public ActionResult> handleHttpProxyRequest() { var builder = java.net.http.HttpClient.newBuilder() .followRedirects(java.net.http.HttpClient.Redirect.NORMAL) .cookieHandler(new CookieManager()) @@ -69,7 +70,7 @@ public class HttpProxyClient extends HttpClient var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); if (response.statusCode() != 200) continue; - return Optional.of(response); + return ActionResult.success(response); } catch (HttpConnectTimeoutException | ConnectException e) { if (proxySettings.isAutoDiscard()) proxySettings.remove(); @@ -85,7 +86,7 @@ public class HttpProxyClient extends HttpClient throw new TikTokLiveRequestException("No more proxies available!"); } - private Optional> handleSocksProxyRequest() { + private ActionResult> handleSocksProxyRequest() { try { SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, new TrustManager[]{ new X509TrustManager() { @@ -117,7 +118,7 @@ public class HttpProxyClient extends HttpClient var response = createHttpResponse(body, toUrl(), responseInfo); - return Optional.of(response); + return ActionResult.success(response); } catch (IOException e) { if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported return super.toResponse(); @@ -133,11 +134,10 @@ public class HttpProxyClient extends HttpClient // Should never be reached! System.out.println("handleSocksProxyRequest: If you see this, message us on discord!"); e.printStackTrace(); - return Optional.empty(); } catch (TikTokLiveRequestException e) { e.printStackTrace(); - return Optional.empty(); } + return ActionResult.failure(); } private ResponseInfo createResponseInfo(int code, Map> headers) { diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java index 1f7da1d..b288c48 100644 --- a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java +++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java @@ -31,6 +31,7 @@ import io.github.jwdeveloper.tiktok.extension.recorder.api.LiveRecorder; import io.github.jwdeveloper.tiktok.extension.recorder.impl.data.*; import io.github.jwdeveloper.tiktok.extension.recorder.impl.enums.LiveQuality; import io.github.jwdeveloper.tiktok.live.LiveClient; +import io.github.jwdeveloper.tiktok.models.ConnectionState; import javax.net.ssl.HttpsURLConnection; import java.io.*; @@ -79,9 +80,10 @@ public class RecorderListener implements LiveRecorder { @TikTokEventObserver private void onConnected(LiveClient liveClient, TikTokConnectedEvent event) { + if (isConnected()) + return; liveDownloadThread = new Thread(() -> { try { - var bufferSize = 1024; var url = new URL(downloadData.getFullUrl()); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); var headers = LiveClientSettings.DefaultRequestHeaders(); @@ -89,18 +91,26 @@ public class RecorderListener implements LiveRecorder { connection.setRequestProperty(entry.getKey(), entry.getValue()); } - var in = new BufferedInputStream(connection.getInputStream()); var path = settings.getOutputPath() + File.separator + settings.getOutputFileName(); var file = new File(path); file.getParentFile().mkdirs(); - var fileOutputStream = new FileOutputStream(file); - byte[] dataBuffer = new byte[bufferSize]; - int bytesRead; - while ((bytesRead = in.read(dataBuffer, 0, bufferSize)) != -1) { - fileOutputStream.write(dataBuffer, 0, bytesRead); + file.createNewFile(); + + try ( + var in = connection.getInputStream(); + var fos = new FileOutputStream(file) + ) { + byte[] dataBuffer = new byte[1024]; + int bytesRead; + while (liveClient.getRoomInfo().getConnectionState() == ConnectionState.CONNECTED && (bytesRead = in.read(dataBuffer)) != -1) { + fos.write(dataBuffer, 0, bytesRead); + fos.flush(); + } + } catch (IOException ignored) { + } finally { + liveClient.getLogger().severe("Stopped recording "+liveClient.getRoomInfo().getHostName()); } - in.close(); - } catch (Exception e) { + } catch (Exception e) { e.printStackTrace(); } }); @@ -108,27 +118,14 @@ public class RecorderListener implements LiveRecorder { liveDownloadThread.start(); } - private static void downloadUsingStream(String urlStr, String file) throws IOException { - URL url = new URL(urlStr); - BufferedInputStream bis = new BufferedInputStream(url.openStream()); - FileOutputStream fis = new FileOutputStream(file); - byte[] buffer = new byte[1024]; - int count; - while ((count = bis.read(buffer, 0, 1024)) != -1) - fis.write(buffer, 0, count); - fis.close(); - bis.close(); - } - - @TikTokEventObserver private void onDisconnected(LiveClient liveClient, TikTokDisconnectedEvent event) { - if (isConnected()) + if (isConnected() && settings.isStopOnDisconnect()) liveDownloadThread.interrupt(); } @TikTokEventObserver - private void onDisconnected(LiveClient liveClient, TikTokLiveEndedEvent event) { + private void onLiveEnded(LiveClient liveClient, TikTokLiveEndedEvent event) { if (isConnected()) liveDownloadThread.interrupt(); } diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/data/RecorderSettings.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/data/RecorderSettings.java index d7db820..2b04f01 100644 --- a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/data/RecorderSettings.java +++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/data/RecorderSettings.java @@ -32,6 +32,7 @@ import java.util.function.Function; @Getter @Setter public class RecorderSettings { + private String ffmpegPath; private String quality; private String format; @@ -39,6 +40,7 @@ public class RecorderSettings { private String outputFileName; private Function prepareDownloadData; private boolean startOnConnected; + private boolean stopOnDisconnect = true; public static RecorderSettings DEFAULT() { return new RecorderSettings();