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