Compare commits

...

5 Commits

Author SHA1 Message Date
JW
15d6351d65 Changes:
- Fixed bug: library was not working on certain java versions
   due to different WebSocket implementation. Instead of using java websocket api now there is `org.java-websocket`
2023-08-24 16:36:40 +02:00
JW
44ba999b83 Update README.md 2023-08-23 21:56:08 +02:00
JW
5cf0d30962 Update README.md 2023-08-23 21:10:12 +02:00
JW
71ebc6e05e Update README.md 2023-08-23 21:00:37 +02:00
GitHub Action
c6d09927a0 Update version in pom.xml 2023-08-23 18:57:15 +00:00
10 changed files with 108 additions and 82 deletions

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.11-Release</version> <version>0.0.12-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>API</artifactId> <artifactId>API</artifactId>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.11-Release</version> <version>0.0.12-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -29,6 +29,23 @@
<artifactId>protobuf-java</artifactId> <artifactId>protobuf-java</artifactId>
<version>3.24.1</version> <version>3.24.1</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.7</version>
<scope>runtime</scope>
</dependency>
</dependencies> </dependencies>

View File

@@ -70,7 +70,6 @@ public class TikTokLiveClientBuilder implements TikTokEventBuilder<TikTokLiveCli
var tiktokRoomInfo = new TikTokRoomInfo(); var tiktokRoomInfo = new TikTokRoomInfo();
tiktokRoomInfo.setUserName(clientSettings.getHostName()); tiktokRoomInfo.setUserName(clientSettings.getHostName());
var cookieJar = new TikTokCookieJar(); var cookieJar = new TikTokCookieJar();
var requestFactory = new TikTokHttpRequestFactory(cookieJar); var requestFactory = new TikTokHttpRequestFactory(cookieJar);
var apiClient = new TikTokHttpApiClient(cookieJar, requestFactory); var apiClient = new TikTokHttpApiClient(cookieJar, requestFactory);
@@ -79,7 +78,6 @@ public class TikTokLiveClientBuilder implements TikTokEventBuilder<TikTokLiveCli
var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler, clientSettings, logger, giftManager, tiktokRoomInfo); var webResponseHandler = new TikTokMessageHandlerRegistration(tikTokEventHandler, clientSettings, logger, giftManager, tiktokRoomInfo);
var webSocketClient = new TikTokWebSocketClient(logger, var webSocketClient = new TikTokWebSocketClient(logger,
cookieJar, cookieJar,
requestFactory,
clientSettings, clientSettings,
webResponseHandler, webResponseHandler,
tikTokEventHandler); tikTokEventHandler);

View File

@@ -11,10 +11,10 @@ import io.github.jwdeveloper.tiktok.http.HttpUtils;
import io.github.jwdeveloper.tiktok.http.TikTokCookieJar; import io.github.jwdeveloper.tiktok.http.TikTokCookieJar;
import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory; import io.github.jwdeveloper.tiktok.http.TikTokHttpRequestFactory;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse; import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import org.java_websocket.client.WebSocketClient;
import java.net.URI; import java.net.URI;
import java.net.http.WebSocket; import java.net.http.WebSocket;
import java.time.Duration;
import java.util.HashMap; import java.util.HashMap;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -23,24 +23,21 @@ public class TikTokWebSocketClient {
private final Logger logger; private final Logger logger;
private final ClientSettings clientSettings; private final ClientSettings clientSettings;
private final TikTokCookieJar tikTokCookieJar; private final TikTokCookieJar tikTokCookieJar;
private final TikTokHttpRequestFactory factory;
private final TikTokMessageHandlerRegistration webResponseHandler; private final TikTokMessageHandlerRegistration webResponseHandler;
private final TikTokEventHandler tikTokEventHandler; private final TikTokEventHandler tikTokEventHandler;
private WebSocket webSocket; private WebSocketClient webSocketClient;
private boolean isConnected; private boolean isConnected;
private TikTokLiveClient tikTokLiveClient; private TikTokLiveClient tikTokLiveClient;
public TikTokWebSocketClient(Logger logger, public TikTokWebSocketClient(Logger logger,
TikTokCookieJar tikTokCookieJar, TikTokCookieJar tikTokCookieJar,
TikTokHttpRequestFactory factory,
ClientSettings clientSettings, ClientSettings clientSettings,
TikTokMessageHandlerRegistration webResponseHandler, TikTokMessageHandlerRegistration webResponseHandler,
TikTokEventHandler tikTokEventHandler) { TikTokEventHandler tikTokEventHandler) {
this.logger = logger; this.logger = logger;
this.tikTokCookieJar = tikTokCookieJar; this.tikTokCookieJar = tikTokCookieJar;
this.clientSettings = clientSettings; this.clientSettings = clientSettings;
this.factory = factory;
this.webResponseHandler = webResponseHandler; this.webResponseHandler = webResponseHandler;
this.tikTokEventHandler = tikTokEventHandler; this.tikTokEventHandler = tikTokEventHandler;
isConnected = false; isConnected = false;
@@ -56,12 +53,12 @@ public class TikTokWebSocketClient {
} }
try { try {
var url = getWebSocketUrl(webcastResponse); var url = getWebSocketUrl(webcastResponse);
if (clientSettings.isHandleExistingMessagesOnConnect()) if (clientSettings.isHandleExistingMessagesOnConnect()) {
{
logger.info("Handling existing messages"); logger.info("Handling existing messages");
webResponseHandler.handle(tikTokLiveClient, webcastResponse); webResponseHandler.handle(tikTokLiveClient, webcastResponse);
} }
webSocket = startWebSocket(url); webSocketClient = startWebSocket(url);
webSocketClient.connect();
} catch (Exception e) { } catch (Exception e) {
throw new TikTokLiveException("Failed to connect to the websocket", e); throw new TikTokLiveException("Failed to connect to the websocket", e);
} }
@@ -82,20 +79,19 @@ public class TikTokWebSocketClient {
return HttpUtils.parseParametersEncode(url, clone); return HttpUtils.parseParametersEncode(url, clone);
} }
private WebSocket startWebSocket(String url) throws Exception { private WebSocketClient startWebSocket(String url) {
var cookie = tikTokCookieJar.parseCookies(); var cookie = tikTokCookieJar.parseCookies();
var map = new HashMap<String, String>(); var map = new HashMap<String, String>();
map.put("Cookie", cookie); map.put("Cookie", cookie);
return factory.openSocket()
.subprotocols("echo-protocol") return new TikTokWebSocketListener(URI.create(url), map, 3000, webResponseHandler, tikTokEventHandler, tikTokLiveClient);
.connectTimeout(Duration.ofSeconds(15))
.header("Cookie", cookie)
.buildAsync(URI.create(url), new TikTokWebSocketListener(webResponseHandler, tikTokEventHandler, tikTokLiveClient)).get();
} }
public void stop() { public void stop() {
if (isConnected && webSocket != null) { if (isConnected && webSocketClient != null) {
webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "ok"); webSocketClient.close();
} }
} }
} }

View File

@@ -1,6 +1,5 @@
package io.github.jwdeveloper.tiktok.websocket; package io.github.jwdeveloper.tiktok.websocket;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import io.github.jwdeveloper.tiktok.TikTokLiveClient; import io.github.jwdeveloper.tiktok.TikTokLiveClient;
import io.github.jwdeveloper.tiktok.events.messages.TikTokConnectedEvent; import io.github.jwdeveloper.tiktok.events.messages.TikTokConnectedEvent;
@@ -12,71 +11,71 @@ import io.github.jwdeveloper.tiktok.handlers.TikTokMessageHandlerRegistration;
import io.github.jwdeveloper.tiktok.messages.WebcastResponse; import io.github.jwdeveloper.tiktok.messages.WebcastResponse;
import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketAck; import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketAck;
import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage; import io.github.jwdeveloper.tiktok.messages.WebcastWebsocketMessage;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import java.io.ByteArrayOutputStream; import java.net.URI;
import java.net.http.WebSocket; import java.net.http.WebSocket;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletionStage;
public class TikTokWebSocketListener extends WebSocketClient {
public class TikTokWebSocketListener implements java.net.http.WebSocket.Listener {
private final ByteArrayOutputStream accumulatedData = new ByteArrayOutputStream();
private final TikTokMessageHandlerRegistration webResponseHandler; private final TikTokMessageHandlerRegistration webResponseHandler;
private final TikTokEventHandler tikTokEventHandler; private final TikTokEventHandler tikTokEventHandler;
private final TikTokLiveClient tikTokLiveClient; private final TikTokLiveClient tikTokLiveClient;
public TikTokWebSocketListener(TikTokMessageHandlerRegistration webResponseHandler, public TikTokWebSocketListener(URI serverUri,
Map<String, String> httpHeaders,
int connectTimeout,
TikTokMessageHandlerRegistration webResponseHandler,
TikTokEventHandler tikTokEventHandler, TikTokEventHandler tikTokEventHandler,
TikTokLiveClient tikTokLiveClient) { TikTokLiveClient tikTokLiveClient) {
super(serverUri, new Draft_6455(), httpHeaders,connectTimeout);
this.webResponseHandler = webResponseHandler; this.webResponseHandler = webResponseHandler;
this.tikTokEventHandler = tikTokEventHandler; this.tikTokEventHandler = tikTokEventHandler;
this.tikTokLiveClient = tikTokLiveClient; this.tikTokLiveClient = tikTokLiveClient;
} }
@Override @Override
public CompletionStage<?> onBinary(WebSocket webSocket, ByteBuffer data, boolean last) { public void onOpen(ServerHandshake serverHandshake) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokConnectedEvent());
sendPing();
}
@Override
public void onMessage(ByteBuffer bytes)
{
try { try {
var bytes = new byte[data.remaining()]; handleBinary(bytes.array());
data.get(bytes);
accumulatedData.write(bytes);
if (last) {
handleBinary(webSocket, accumulatedData.toByteArray());
accumulatedData.reset();
}
} catch (Exception e) { } catch (Exception e) {
tikTokEventHandler.publish(tikTokLiveClient, new TikTokErrorEvent(e)); tikTokEventHandler.publish(tikTokLiveClient, new TikTokErrorEvent(e));
} }
webSocket.request(1); sendPing();
return null;
} }
@Override @Override
public void onOpen(java.net.http.WebSocket webSocket) { public void onClose(int i, String s, boolean b) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokConnectedEvent());
webSocket.request(1);
}
@Override
public void onError(java.net.http.WebSocket webSocket, Throwable error) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokErrorEvent(error));
webSocket.request(1);
}
@Override
public CompletionStage<?> onClose(java.net.http.WebSocket webSocket, int statusCode, String reason) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokDisconnectedEvent()); tikTokEventHandler.publish(tikTokLiveClient,new TikTokDisconnectedEvent());
return java.net.http.WebSocket.Listener.super.onClose(webSocket, statusCode, reason);
} }
private void handleBinary(WebSocket webSocket, byte[] buffer) { @Override
public void onError(Exception error) {
tikTokEventHandler.publish(tikTokLiveClient,new TikTokErrorEvent(error));
sendPing();
}
private void handleBinary(byte[] buffer) {
var websocketMessageOptional = getWebcastWebsocketMessage(buffer); var websocketMessageOptional = getWebcastWebsocketMessage(buffer);
if (websocketMessageOptional.isEmpty()) { if (websocketMessageOptional.isEmpty()) {
return; return;
} }
var websocketMessage = websocketMessageOptional.get(); var websocketMessage = websocketMessageOptional.get();
sendAckId(webSocket, websocketMessage.getId()); sendAckId(websocketMessage.getId());
var webResponse = getWebResponseMessage(websocketMessage.getBinary()); var webResponse = getWebResponseMessage(websocketMessage.getBinary());
webResponseHandler.handle(tikTokLiveClient, webResponse); webResponseHandler.handle(tikTokLiveClient, webResponse);
@@ -114,13 +113,19 @@ public class TikTokWebSocketListener implements java.net.http.WebSocket.Listener
} }
} }
private void sendAckId(WebSocket webSocket, long id) { private void sendAckId(long id) {
var serverInfo = WebcastWebsocketAck var serverInfo = WebcastWebsocketAck
.newBuilder() .newBuilder()
.setType("ack") .setType("ack")
.setId(id) .setId(id)
.build(); .build();
webSocket.sendBinary(serverInfo.toByteString().asReadOnlyByteBuffer(), true); send(serverInfo.toByteString().asReadOnlyByteBuffer());
} }
}
@Override
public void onMessage(String s) {
}
}

View File

@@ -40,18 +40,21 @@ Do you prefer other programming languages?
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId> <artifactId>protobuf-java</artifactId>
<version>3.24.1</version> <version>3.24.1</version>
<scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.10.1</version> <version>2.10.1</version>
<scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.jwdeveloper</groupId> <groupId>com.github.jwdeveloper</groupId>
<artifactId>TikTok-Live-Java</artifactId> <artifactId>TikTok-Live-Java</artifactId>
<version>0.0.11-Release</version> <version>0.0.12-Release</version>
<scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>
``` ```
@@ -65,23 +68,28 @@ Do you prefer other programming languages?
var tiktokUsername = "jwdevtiktok"; var tiktokUsername = "jwdevtiktok";
TikTokLive.newClient(tiktokUsername) TikTokLive.newClient(tiktokUsername)
.onConnected(event -> .onConnected((client, event) ->
{ {
System.out.println("Connected"); System.out.println("Connected");
}) })
.onJoin(event -> .onJoin((client, event) ->
{ {
System.out.println("User joined -> " + event.getUser().getNickName()); System.out.println("User joined -> " + event.getUser().getNickName());
}) })
.onComment(event -> .onComment((client, event) ->
{ {
System.out.println(event.getUser().getUniqueId() + ": " + event.getText()); System.out.println(event.getUser().getUniqueId() + ": " + event.getText());
}) })
.onError(event -> .onEvent((client, event) ->
{
System.out.println("Viewers count: "+client.getRoomInfo().getViewersCount());
})
.onError((client, event) ->
{ {
event.getException().printStackTrace(); event.getException().printStackTrace();
}) })
.buildAndRun(); .buildAndRun();
System.in.read();
} }
``` ```
## Configuration ## Configuration
@@ -90,35 +98,36 @@ Do you prefer other programming languages?
public class ConfigurationExample public class ConfigurationExample
{ {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
var tiktokUsername = "jwdevtiktok";
TikTokLive.newClient("jwdevtiktok") TikTokLive.newClient(tiktokUsername)
.configure(clientSettings -> .configure(clientSettings ->
{ {
clientSettings.setHostName("jwdevtiktok"); //tiktok user clientSettings.setHostName(Main.TEST_TIKTOK_USER); // TikTok user name
clientSettings.setClientLanguage("en"); //language clientSettings.setClientLanguage("en"); // Language
clientSettings.setTimeout(Duration.ofSeconds(2)); //connection timeout clientSettings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
clientSettings.setLogLevel(Level.ALL); //log level clientSettings.setLogLevel(Level.ALL); // Log level
clientSettings.setDownloadGiftInfo(true); //TODO clientSettings.setDownloadGiftInfo(true); // Downloading meta information about gifts. You can access it by client.getGiftManager().getGiftsInfo();
clientSettings.setCheckForUnparsedData(true); //TODO clientSettings.setPrintMessageData(true); // Printing TikTok Protocol buffer messages in Base64 format
clientSettings.setPollingInterval(Duration.ofSeconds(1)); //TODO clientSettings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
clientSettings.setPrintMessageData(true); //TODO clientSettings.setHandleExistingMessagesOnConnect(true); // Invokes all TikTok events that had occurred before connection
clientSettings.setPrintToConsole(true); //TODO clientSettings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
clientSettings.setHandleExistingMessagesOnConnect(true); //TODO clientSettings.setRetryConnectionTimeout(Duration.ofSeconds(1)); // Timeout before next reconnection
clientSettings.setRetryOnConnectionFailure(true); //TODO
}) })
.buildAndRun(); .buildAndRun();
System.in.read();
} }
} }
``` ```
## Methods ## Methods
A `TikTokLive` object contains the following methods. A `client (LiveClient)` object contains the following methods.
| Method Name | Description | | Method Name | Description |
| ----------- | ----------- | | ----------- | ----------- |
| connect | Connects to the live stream chat.<br>Returns a `Promise` which will be resolved when the connection is successfully established. | | connect | Connects to the live stream chat.<br>Returns a `Promise` which will be resolved when the connection is successfully established. |
| disconnect | Disconnects the connection. | | disconnect | Disconnects the connection. |
| getGiftManager | Gets the meta informations about all gifts. |
| getRoomInfo | Gets the current room info from TikTok API including streamer info, room status and statistics. | | getRoomInfo | Gets the current room info from TikTok API including streamer info, room status and statistics. |
## Events ## Events

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.11-Release</version> <version>0.0.12-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -7,7 +7,7 @@ import java.io.IOException;
public class Main { public class Main {
public static String TEST_TIKTOK_USER = "vadimpyrography"; public static String TEST_TIKTOK_USER = "dmikl_";
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
var client = TikTokLive.newClient(TEST_TIKTOK_USER) var client = TikTokLive.newClient(TEST_TIKTOK_USER)

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<version>0.0.11-Release</version> <version>0.0.12-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -7,7 +7,7 @@
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>0.0.11-Release</version> <version>0.0.12-Release</version>
<modules> <modules>
<module>API</module> <module>API</module>
<module>Client</module> <module>Client</module>
@@ -54,6 +54,7 @@
<configuration> <configuration>
<shadedArtifactAttached>true</shadedArtifactAttached> <shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>all</shadedClassifierName> <shadedClassifierName>all</shadedClassifierName>
<createDependencyReducedPom>false</createDependencyReducedPom>
<minimizeJar>true</minimizeJar> <minimizeJar>true</minimizeJar>
<artifactSet> <artifactSet>
<includes> <includes>
@@ -64,8 +65,6 @@
<filter> <filter>
<artifact>*:*</artifact> <artifact>*:*</artifact>
<excludes> <excludes>
<exclude>**/proto/**</exclude>
<exclude>**/google/**</exclude>
<exclude>**/tiktokSchema.proto/**</exclude> <exclude>**/tiktokSchema.proto/**</exclude>
</excludes> </excludes>
</filter> </filter>
@@ -99,6 +98,8 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>