diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/control/TikTokConnectingEvent.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/control/TikTokConnectingEvent.java
new file mode 100644
index 00000000..86536a81
--- /dev/null
+++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/control/TikTokConnectingEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.data.events.control;
+
+import io.github.jwdeveloper.tiktok.annotations.EventMeta;
+import io.github.jwdeveloper.tiktok.annotations.EventType;
+import io.github.jwdeveloper.tiktok.data.events.common.TikTokLiveClientEvent;
+
+
+/**
+ * Triggered when client is connecting to live is successfully established.
+ */
+@EventMeta(eventType = EventType.Control)
+public class TikTokConnectingEvent extends TikTokLiveClientEvent
+{
+
+}
diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/http/TikTokRoomDataResponseEvent.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/http/TikTokRoomDataResponseEvent.java
new file mode 100644
index 00000000..8195973c
--- /dev/null
+++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/http/TikTokRoomDataResponseEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.data.events.http;
+
+import io.github.jwdeveloper.tiktok.annotations.EventMeta;
+import io.github.jwdeveloper.tiktok.annotations.EventType;
+import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
+import io.github.jwdeveloper.tiktok.data.requests.LiveData;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+@EventMeta(eventType = EventType.Debug)
+public class TikTokRoomDataResponseEvent extends TikTokEvent
+{
+ private final LiveData.Response liveData;
+}
diff --git a/Client/pom.xml b/Client/pom.xml
index ec21e033..f4ba06c3 100644
--- a/Client/pom.xml
+++ b/Client/pom.xml
@@ -39,7 +39,7 @@
org.java-websocket
Java-WebSocket
- 1.5.4
+ 1.5.5
org.testng
diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLive.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLive.java
index d2243195..2d67e890 100644
--- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLive.java
+++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLive.java
@@ -50,7 +50,6 @@ public static boolean isLiveOnline(String hostName)
return requests().fetchLiveUserData(hostName).isLiveOnline();
}
-
/**
*
* @param hostName profile name of Tiktok user could be found in profile link
diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java
index 81709a07..f0741449 100644
--- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java
+++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java
@@ -26,6 +26,8 @@
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokReconnectingEvent;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
+import io.github.jwdeveloper.tiktok.data.events.control.TikTokConnectingEvent;
+import io.github.jwdeveloper.tiktok.data.events.http.TikTokRoomDataResponseEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
@@ -120,7 +122,7 @@ public void tryConnect() {
}
setState(ConnectionState.CONNECTING);
-
+ tikTokEventHandler.publish(this,new TikTokConnectingEvent());
var userDataRequest = new LiveUserData.Request(liveRoomInfo.getHostName());
var userData = httpClient.fetchLiveUserData(userDataRequest);
liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp());
@@ -134,6 +136,7 @@ public void tryConnect() {
var liveDataRequest = new LiveData.Request(userData.getRoomId());
var liveData = httpClient.fetchLiveData(liveDataRequest);
+ tikTokEventHandler.publish(this, new TikTokRoomDataResponseEvent(liveData));
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) {
throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found.");
}
@@ -159,8 +162,8 @@ public void disconnect() {
if (liveRoomInfo.hasConnectionState(ConnectionState.DISCONNECTED)) {
return;
}
- webSocketClient.stop();
setState(ConnectionState.DISCONNECTED);
+ webSocketClient.stop();
}
private void setState(ConnectionState connectionState) {
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 88b523c5..d17c4129 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
@@ -99,7 +99,6 @@ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {}
try {
Proxy proxy = new Proxy(Proxy.Type.SOCKS, proxySettings.next().toSocketAddress());
- System.err.println("Connecting to "+ url);
HttpsURLConnection socksConnection = (HttpsURLConnection) url.openConnection(proxy);
socksConnection.setSSLSocketFactory(sc.getSocketFactory());
socksConnection.setConnectTimeout(httpClientSettings.getTimeout().toMillisPart());
diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java
index 1a9108bc..34041b44 100644
--- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java
+++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java
@@ -43,6 +43,7 @@ public class LiveDataMapper {
public LiveData.Response map(String json) {
var response = new LiveData.Response();
+ response.setJson(json);
var parsedJson = JsonParser.parseString(json);
var jsonObject = parsedJson.getAsJsonObject();
diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokListenersManager.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokListenersManager.java
index 16c1d968..27310637 100644
--- a/Client/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokListenersManager.java
+++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokListenersManager.java
@@ -111,6 +111,7 @@ private ListenerBindingModel bindToEvents(TikTokEventListener listener) {
EventConsumer eventMethodRef = (liveClient, event) ->
{
try {
+ method.setAccessible(true);
method.invoke(listener, liveClient, event);
} catch (Exception e) {
throw new TikTokEventListenerMethodException(e);
diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketClient.java
index 5b32b765..365fcc33 100644
--- a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketClient.java
+++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketClient.java
@@ -22,7 +22,6 @@
*/
package io.github.jwdeveloper.tiktok.websocket;
-
import io.github.jwdeveloper.tiktok.*;
import io.github.jwdeveloper.tiktok.data.dto.ProxyData;
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
@@ -41,6 +40,8 @@ public class TikTokWebSocketClient implements SocketClient {
private final TikTokLiveMessageHandler messageHandler;
private final TikTokLiveEventHandler tikTokEventHandler;
private WebSocketClient webSocketClient;
+
+ private TikTokWebSocketPingingTask pingingTask;
private boolean isConnected;
public TikTokWebSocketClient(
@@ -51,11 +52,11 @@ public TikTokWebSocketClient(
this.messageHandler = messageHandler;
this.tikTokEventHandler = tikTokEventHandler;
isConnected = false;
+ pingingTask = new TikTokWebSocketPingingTask();
}
@Override
- public void start(LiveConnectionData.Response connectionData, LiveClient liveClient)
- {
+ public void start(LiveConnectionData.Response connectionData, LiveClient liveClient) {
if (isConnected) {
stop();
}
@@ -74,16 +75,16 @@ public void start(LiveConnectionData.Response connectionData, LiveClient liveCli
// ProxyClientSettings proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
// if (proxyClientSettings.isEnabled())
// connectProxy(proxyClientSettings);
- // else
- connectDefault();
+ // else
+ connectDefault();
}
private void connectDefault() {
try {
webSocketClient.connect();
+ pingingTask.run(webSocketClient);
isConnected = true;
- } catch (Exception e)
- {
+ } catch (Exception e) {
isConnected = false;
throw new TikTokLiveException("Failed to connect to the websocket", e);
}
@@ -105,21 +106,26 @@ public void connectProxy(ProxyClientSettings proxySettings) {
}
public boolean tryProxyConnection(ProxyClientSettings proxySettings, ProxyData proxyData) {
- webSocketClient.setProxy(new Proxy(proxySettings.getType(), proxyData.toSocketAddress()));
try {
if (proxySettings.getType() == Proxy.Type.SOCKS) {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[]{new X509TrustManager() {
- public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {}
- public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {}
- public X509Certificate[] getAcceptedIssuers() { return null; }
+ public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
+ }
+
+ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
}}, null);
webSocketClient.setSocketFactory(sc.getSocketFactory());
}
+ webSocketClient.setProxy(new Proxy(proxySettings.getType(), proxyData.toSocketAddress()));
webSocketClient.connect();
return true;
- } catch (Exception e)
- {
+ } catch (Exception e) {
return false;
}
}
@@ -127,6 +133,7 @@ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {}
public void stop() {
if (isConnected && webSocketClient != null && webSocketClient.isOpen()) {
webSocketClient.closeConnection(0, "");
+ pingingTask.stop();
}
webSocketClient = null;
isConnected = false;
diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketPingingTask.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketPingingTask.java
new file mode 100644
index 00000000..be104067
--- /dev/null
+++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketPingingTask.java
@@ -0,0 +1,60 @@
+package io.github.jwdeveloper.tiktok.websocket;
+
+import org.java_websocket.WebSocket;
+
+import java.util.Random;
+
+public class TikTokWebSocketPingingTask
+{
+ private Thread thread;
+ private boolean isRunning = false;
+ private final int MIN_TIMEOUT = 250;
+ private final int MAX_TIMEOUT = 500;
+
+
+ public void run(WebSocket webSocket)
+ {
+ stop();
+ thread = new Thread(() ->
+ {
+ pingTask(webSocket);
+ });
+ isRunning =true;
+ thread.start();
+ }
+
+ public void stop()
+ {
+ if(thread != null)
+ {
+ thread.interrupt();
+ }
+ isRunning = false;
+ }
+
+
+ private void pingTask(WebSocket webSocket)
+ {
+ var random = new Random();
+ while (isRunning)
+ {
+ try
+ {
+ if(!webSocket.isOpen())
+ {
+ Thread.sleep(100);
+ continue;
+ }
+ webSocket.sendPing();
+
+ var timeout = random.nextInt(MAX_TIMEOUT)+MIN_TIMEOUT;
+ Thread.sleep(timeout);
+ }
+ catch (Exception e)
+ {
+ isRunning = false;
+ }
+ }
+
+ }
+}
diff --git a/Examples/pom.xml b/Examples/pom.xml
index 33d117a0..7b41c54d 100644
--- a/Examples/pom.xml
+++ b/Examples/pom.xml
@@ -60,6 +60,18 @@
${project.version}
compile
+
+ io.github.jwdeveloper.tiktok
+ extension-collector
+ 1.0.14-Release
+ compile
+
+
+ io.github.jwdeveloper.tiktok
+ extension-recorder
+ 1.0.14-Release
+ compile
+
diff --git a/Examples/src/main/java/io/github/jwdeveloper/tiktok/CollectorExample.java b/Examples/src/main/java/io/github/jwdeveloper/tiktok/CollectorExample.java
new file mode 100644
index 00000000..e00f6cd3
--- /dev/null
+++ b/Examples/src/main/java/io/github/jwdeveloper/tiktok/CollectorExample.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok;
+
+import io.github.jwdeveloper.tiktok.extension.collector.TikTokLiveCollector;
+
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+public class CollectorExample {
+
+
+ private static String mongoUser;
+
+ private static String mongoPassword;
+
+ private static String mongoDatabase;
+
+ public static void main(String[] args) throws IOException {
+
+ var collector = TikTokLiveCollector.use(settings ->
+ {
+ settings.setConnectionUrl("mongodb+srv://" + mongoUser + ":" + mongoPassword + "@" + mongoDatabase + "/?retryWrites=true&w=majority");
+ settings.setDatabaseName("tiktok");
+ });
+ collector.connectDatabase();
+
+ var users = List.of("tehila_723", "dino123597", "domaxyzx", "dash4214", "obserwacje_live");
+ var sessionTag = "Dupa";
+ for (var user : users) {
+ TikTokLive.newClient(user)
+ .configure(liveClientSettings ->
+ {
+ liveClientSettings.setPrintToConsole(true);
+ })
+ .onError((liveClient, event) ->
+ {
+ event.getException().printStackTrace();
+ })
+ .addListener(collector.newListener(Map.of("sessionTag", sessionTag), document ->
+ {
+ if (document.get("dataType") == "message") {
+ return false;
+ }
+ return true;
+ }))
+ .buildAndConnectAsync();
+ }
+
+ System.in.read();
+ collector.disconnectDatabase();
+ }
+}
diff --git a/Examples/src/main/java/io/github/jwdeveloper/tiktok/ListenerExample.java b/Examples/src/main/java/io/github/jwdeveloper/tiktok/ListenerExample.java
index a9aa3dd1..f4cde537 100644
--- a/Examples/src/main/java/io/github/jwdeveloper/tiktok/ListenerExample.java
+++ b/Examples/src/main/java/io/github/jwdeveloper/tiktok/ListenerExample.java
@@ -57,7 +57,7 @@ public static void main(String[] args) throws IOException {
/**
*
* Method in TikTokEventListener should meet 4 requirements to be detected
- * - must have @TikTokEventHandler annotation
+ * - must have @TikTokEventObserver annotation
* - must have 2 parameters
* - first parameter must be LiveClient
* - second must be class that extending TikTokEvent
diff --git a/Examples/src/main/java/io/github/jwdeveloper/tiktok/ProxyExample.java b/Examples/src/main/java/io/github/jwdeveloper/tiktok/ProxyExample.java
index 3746bf43..c9113547 100644
--- a/Examples/src/main/java/io/github/jwdeveloper/tiktok/ProxyExample.java
+++ b/Examples/src/main/java/io/github/jwdeveloper/tiktok/ProxyExample.java
@@ -24,8 +24,7 @@
import java.net.Proxy;
-public class ProxyExample
-{
+public class ProxyExample {
public static void main(String[] args) throws Exception {
TikTokLive.newClient(SimpleExample.TIKTOK_HOSTNAME)
.configure(clientSettings -> {
@@ -38,13 +37,16 @@ public static void main(String[] args) throws Exception {
})
.onConnected((liveClient, event) ->
liveClient.getLogger().info("Connected "+liveClient.getRoomInfo().getHostName()))
+ .onComment((liveClient, event) -> liveClient.getLogger().info(event.getUser().getName()+": "+event.getText()))
+ .onLike((liveClient, event) -> liveClient.getLogger().info(event.getUser().getName()+" sent "+event.getLikes()+"x likes!"))
.onDisconnected((liveClient, event) ->
liveClient.getLogger().info("Disconnect reason: "+event.getReason()))
.onLiveEnded((liveClient, event) ->
- liveClient.getLogger().info("Live Ended"))
+ liveClient.getLogger().info("Live Ended: "+liveClient.getRoomInfo().getHostName()))
.onError((liveClient, event) ->
event.getException().printStackTrace())
.buildAndConnect();
+
System.in.read();
}
}
\ No newline at end of file
diff --git a/Examples/src/main/java/io/github/jwdeveloper/tiktok/RecorderExample.java b/Examples/src/main/java/io/github/jwdeveloper/tiktok/RecorderExample.java
new file mode 100644
index 00000000..50b05de6
--- /dev/null
+++ b/Examples/src/main/java/io/github/jwdeveloper/tiktok/RecorderExample.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok;
+
+
+import io.github.jwdeveloper.tiktok.extension.recorder.TikTokLiveRecorder;
+import io.github.jwdeveloper.tiktok.extension.recorder.impl.event.TikTokLiveRecorderStartedEvent;
+
+public class RecorderExample {
+
+ public static void main(String[] args) {
+
+ TikTokLive.newClient("bangbetmenygy")
+ .configure(liveClientSettings ->
+ {
+ liveClientSettings.setPrintToConsole(true);
+ })
+ .onError((liveClient, event) ->
+ {
+ event.getException().printStackTrace();
+ })
+ .addListener(TikTokLiveRecorder.use(recorderSettings ->
+ {
+ recorderSettings.setFfmpegPath("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\extension-recorder\\libs\\ffmpeg.exe");
+ recorderSettings.setOutputPath("C:\\Users\\ja\\IdeaProjects\\TikTokLiveJava\\extension-recorder\\out");
+ recorderSettings.setOutputFileName("test.flv");
+ }))
+ .onEvent(TikTokLiveRecorderStartedEvent.class, (liveClient, event) ->
+ {
+ System.out.println(event.getDownloadData().getFullUrl());
+ })
+ .buildAndConnect();
+
+ }
+}
diff --git a/Examples/src/main/java/io/github/jwdeveloper/tiktok/SimpleExample.java b/Examples/src/main/java/io/github/jwdeveloper/tiktok/SimpleExample.java
index e1df391a..858ff8f5 100644
--- a/Examples/src/main/java/io/github/jwdeveloper/tiktok/SimpleExample.java
+++ b/Examples/src/main/java/io/github/jwdeveloper/tiktok/SimpleExample.java
@@ -23,27 +23,18 @@
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.data.events.TikTokSubNotifyEvent;
-import io.github.jwdeveloper.tiktok.data.events.TikTokSubscribeEvent;
import io.github.jwdeveloper.tiktok.data.events.envelop.TikTokChestEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
-import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
-import io.github.jwdeveloper.tiktok.messages.webcast.WebcastGiftMessage;
import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
-import io.github.jwdeveloper.tiktok.utils.JsonUtil;
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.HashMap;
import java.util.logging.Level;
public class SimpleExample {
- public static String TIKTOK_HOSTNAME = "dash4214";
+ public static String TIKTOK_HOSTNAME = "dash4114";
- public static void main(String[] args) throws IOException, InterruptedException {
+ public static void main(String[] args) throws IOException {
showLogo();
@@ -160,4 +151,4 @@ private static void showLogo() {
""");
}
-}
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index f5ba958a..a0d7ac72 100644
--- a/README.md
+++ b/README.md
@@ -652,24 +652,24 @@ public static void main(String[] args) throws IOException {
public static class CustomListener implements TikTokEventListener {
- @TikTokEventHandler
+ @TikTokEventObserver
public void onLike(LiveClient liveClient, TikTokLikeEvent event) {
System.out.println(event.toString());
}
- @TikTokEventHandler
+ @TikTokEventObserver
public void onError(LiveClient liveClient, TikTokErrorEvent event) {
// event.getException().printStackTrace();
}
- @TikTokEventHandler
+ @TikTokEventObserver
public void onComment(LiveClient liveClient, TikTokCommentEvent event) {
var userName = event.getUser().getName();
var text = event.getText();
liveClient.getLogger().info(userName + ": " + text);
}
- @TikTokEventHandler
+ @TikTokEventObserver
public void onGift(LiveClient liveClient, TikTokGiftEvent event) {
var message = switch (event.getGift()) {
case ROSE -> "Thanks :)";
diff --git a/extension-collector/README.md b/extension-collector/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/extension-collector/pom.xml b/extension-collector/pom.xml
new file mode 100644
index 00000000..c5031d53
--- /dev/null
+++ b/extension-collector/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+ io.github.jwdeveloper.tiktok
+ TikTokLiveJava
+ 1.0.14-Release
+
+
+
+ extension-collector
+
+
+
+
+ io.github.jwdeveloper.tiktok
+ API
+ ${project.version}
+ compile
+
+
+ com.google.protobuf
+ protobuf-java
+ 3.24.1
+
+
+ org.mongodb
+ mongodb-driver-sync
+ 4.4.0
+
+
+ io.github.jwdeveloper.tiktok
+ API
+ 1.0.17-Release
+ compile
+
+
+
+
\ No newline at end of file
diff --git a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/TikTokLiveCollector.java b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/TikTokLiveCollector.java
new file mode 100644
index 00000000..0ba18942
--- /dev/null
+++ b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/TikTokLiveCollector.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.extension.collector;
+
+import io.github.jwdeveloper.tiktok.extension.collector.api.LiveDataCollector;
+import io.github.jwdeveloper.tiktok.extension.collector.api.data.LiveDataCollectorSettings;
+import io.github.jwdeveloper.tiktok.extension.collector.impl.TikTokLiveDataCollector;
+
+import java.util.function.Consumer;
+
+public class TikTokLiveCollector
+{
+
+ public static TikTokLiveDataCollector use(Consumer consumer)
+ {
+ var settings = new LiveDataCollectorSettings();
+ consumer.accept(settings);
+ return new TikTokLiveDataCollector(settings);
+ }
+}
diff --git a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/LiveDataCollector.java b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/LiveDataCollector.java
new file mode 100644
index 00000000..0532f244
--- /dev/null
+++ b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/LiveDataCollector.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.extension.collector.api;
+
+import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
+
+public interface LiveDataCollector extends TikTokEventListener
+{
+
+}
diff --git a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/data/CollectorListenerSettings.java b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/data/CollectorListenerSettings.java
new file mode 100644
index 00000000..8399a05a
--- /dev/null
+++ b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/data/CollectorListenerSettings.java
@@ -0,0 +1,13 @@
+package io.github.jwdeveloper.tiktok.extension.collector.api.data;
+
+import lombok.Data;
+import org.bson.Document;
+
+import java.util.Map;
+import java.util.function.Function;
+
+@Data
+public class CollectorListenerSettings {
+ private Map extraFields;
+ private Function filter;
+}
diff --git a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/data/LiveDataCollectorSettings.java b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/data/LiveDataCollectorSettings.java
new file mode 100644
index 00000000..4597c5cc
--- /dev/null
+++ b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/data/LiveDataCollectorSettings.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.extension.collector.api.data;
+
+import lombok.Data;
+
+import java.util.function.Consumer;
+
+@Data
+public class LiveDataCollectorSettings {
+
+ private String connectionUrl;
+
+ private String databaseName;
+
+ private String sessionTag;
+
+
+ public void setConnectionUrl(String connectionUrl) {
+ this.connectionUrl = connectionUrl;
+ }
+
+ public void setConnectionUrl(Consumer consumer) {
+ var builder = new MongoDBConnectionStringBuilder();
+ consumer.accept(builder);
+ connectionUrl = builder.build();
+ }
+}
diff --git a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/data/MongoDBConnectionStringBuilder.java b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/data/MongoDBConnectionStringBuilder.java
new file mode 100644
index 00000000..556a0cd2
--- /dev/null
+++ b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/data/MongoDBConnectionStringBuilder.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.extension.collector.api.data;
+
+public class MongoDBConnectionStringBuilder {
+ private String username;
+ private String password;
+ private String database;
+ private String cluster;
+
+ public MongoDBConnectionStringBuilder setUsername(String username) {
+ this.username = username;
+ return this;
+ }
+
+ public MongoDBConnectionStringBuilder setPassword(String password) {
+ this.password = password;
+ return this;
+ }
+
+ public MongoDBConnectionStringBuilder setDatabase(String database) {
+ this.database = database;
+ return this;
+ }
+
+ public MongoDBConnectionStringBuilder setCluster(String cluster) {
+ this.cluster = cluster;
+ return this;
+ }
+
+ public String build() {
+ return String.format("mongodb+srv://%s:%s@%s/%s?retryWrites=true&w=majority",
+ username, password, cluster, database);
+ }
+}
diff --git a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/TikTokLiveDataCollector.java b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/TikTokLiveDataCollector.java
new file mode 100644
index 00000000..d9899b72
--- /dev/null
+++ b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/TikTokLiveDataCollector.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.extension.collector.impl;
+
+import com.mongodb.ConnectionString;
+import com.mongodb.MongoClientSettings;
+import com.mongodb.ServerApi;
+import com.mongodb.ServerApiVersion;
+import com.mongodb.client.MongoClient;
+import com.mongodb.client.MongoClients;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import com.mongodb.client.model.Indexes;
+import io.github.jwdeveloper.tiktok.extension.collector.api.data.CollectorListenerSettings;
+import io.github.jwdeveloper.tiktok.extension.collector.api.data.LiveDataCollectorSettings;
+import org.bson.Document;
+
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.Function;
+
+public class TikTokLiveDataCollector {
+
+ private final LiveDataCollectorSettings settings;
+ private MongoClient mongoClient;
+ private MongoDatabase database;
+ private MongoCollection collection;
+
+ public TikTokLiveDataCollector(LiveDataCollectorSettings settings) {
+ this.settings = settings;
+ }
+
+
+ public void connectDatabase() {
+ var serverApi = ServerApi.builder()
+ .version(ServerApiVersion.V1)
+ .build();
+ var mongoSettings = MongoClientSettings.builder()
+ .applyConnectionString(new ConnectionString(settings.getConnectionUrl()))
+ .serverApi(serverApi)
+ .build();
+
+ mongoClient = MongoClients.create(mongoSettings);
+ database = mongoClient.getDatabase(settings.getDatabaseName());
+ collection = database.getCollection("data");
+ collection.createIndex(Indexes.hashed("session"));
+ collection.createIndex(Indexes.hashed("dataType"));
+ }
+
+
+ public void disconnectDatabase() {
+ mongoClient.close();
+ }
+
+ public TikTokLiveDataCollectorListener newListener() {
+ return newListener(Map.of());
+ }
+
+ public TikTokLiveDataCollectorListener newListener(Map additionalFields) {
+ return newListener(additionalFields, (e)->true);
+ }
+
+ public TikTokLiveDataCollectorListener newListener(Map additionalFields,
+ Function filter) {
+ var settings = new CollectorListenerSettings();
+ settings.setExtraFields(additionalFields);
+ settings.setFilter(filter);
+ return new TikTokLiveDataCollectorListener(collection, settings);
+ }
+}
diff --git a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/TikTokLiveDataCollectorListener.java b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/TikTokLiveDataCollectorListener.java
new file mode 100644
index 00000000..17d9578d
--- /dev/null
+++ b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/impl/TikTokLiveDataCollectorListener.java
@@ -0,0 +1,122 @@
+package io.github.jwdeveloper.tiktok.extension.collector.impl;
+
+import com.mongodb.client.MongoCollection;
+import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver;
+import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
+import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
+import io.github.jwdeveloper.tiktok.data.events.control.TikTokConnectingEvent;
+import io.github.jwdeveloper.tiktok.data.events.websocket.TikTokWebsocketResponseEvent;
+import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveMessageException;
+import io.github.jwdeveloper.tiktok.extension.collector.api.LiveDataCollector;
+import io.github.jwdeveloper.tiktok.extension.collector.api.data.CollectorListenerSettings;
+import io.github.jwdeveloper.tiktok.live.LiveClient;
+import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
+import io.github.jwdeveloper.tiktok.utils.JsonUtil;
+import org.bson.Document;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Base64;
+import java.util.UUID;
+
+public class TikTokLiveDataCollectorListener implements LiveDataCollector {
+
+ private final MongoCollection collection;
+ private final CollectorListenerSettings settings;
+ private String sessionId;
+ private String userName;
+
+ public TikTokLiveDataCollectorListener(MongoCollection collection, CollectorListenerSettings settings) {
+ this.collection = collection;
+ this.settings = settings;
+ }
+
+
+ @TikTokEventObserver
+ private void onResponse(LiveClient liveClient, TikTokWebsocketResponseEvent event) {
+ includeResponse(liveClient, event.getResponse());
+ event.getResponse().getMessagesList().forEach(message ->
+ {
+ includeMessage(liveClient, message);
+ });
+ }
+
+ @TikTokEventObserver
+ private void onEvent(LiveClient liveClient, TikTokEvent event) {
+ if (event instanceof TikTokConnectingEvent) {
+ sessionId = UUID.randomUUID().toString();
+ userName = liveClient.getRoomInfo().getHostName();
+ }
+
+ if (event instanceof TikTokErrorEvent) {
+ return;
+ }
+
+ includeEvent(event);
+ }
+
+ @TikTokEventObserver
+ private void onError(LiveClient liveClient, TikTokErrorEvent event) {
+ event.getException().printStackTrace();
+ includeError(event);
+ }
+
+
+ private void includeResponse(LiveClient liveClient, WebcastResponse message) {
+ var messageContent = Base64.getEncoder().encodeToString(message.toByteArray());
+ insertDocument(createDocument("response", "webcast", messageContent));
+ }
+
+ private void includeMessage(LiveClient liveClient, WebcastResponse.Message message) {
+ var method = message.getMethod();
+ var messageContent = Base64.getEncoder().encodeToString(message.getPayload().toByteArray());
+
+ insertDocument(createDocument("message", method, messageContent));
+ }
+
+ private void includeEvent(TikTokEvent event) {
+ var json = JsonUtil.toJson(event);
+ var content = Base64.getEncoder().encodeToString(json.getBytes());
+ var name = event.getClass().getSimpleName();
+ insertDocument(createDocument("event", name, content));
+ }
+
+ private void includeError(TikTokErrorEvent event) {
+ var exception = event.getException();
+ var exceptionName = event.getException().getClass().getSimpleName();
+
+ var sw = new StringWriter();
+ var pw = new PrintWriter(sw);
+ event.getException().printStackTrace(pw);
+ var content = sw.toString();
+
+ var doc = createDocument("error", exceptionName, content);
+ if (exception instanceof TikTokLiveMessageException ex) {
+ doc.append("message", ex.messageToBase64())
+ .append("response", ex.webcastResponseToBase64());
+ }
+ insertDocument(doc);
+ }
+
+
+ private void insertDocument(Document document) {
+ if (!settings.getFilter().apply(document)) {
+ return;
+ }
+ collection.insertOne(document);
+ }
+
+
+ private Document createDocument(String dataType, String dataTypeName, String content) {
+ var doc = new Document();
+ doc.append("session", sessionId);
+ for (var entry : settings.getExtraFields().entrySet()) {
+ doc.append(entry.getKey(), entry.getValue());
+ }
+ doc.append("tiktokUser", userName);
+ doc.append("dataType", dataType);
+ doc.append("dataTypeName", dataTypeName);
+ doc.append("content", content);
+ return doc;
+ }
+}
diff --git a/extension-recorder/README.md b/extension-recorder/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/extension-recorder/pom.xml b/extension-recorder/pom.xml
new file mode 100644
index 00000000..6a9dd469
--- /dev/null
+++ b/extension-recorder/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+ TikTokLiveJava
+ io.github.jwdeveloper.tiktok
+ 1.0.14-Release
+
+ 4.0.0
+ extension-recorder
+
+
+ io.github.jwdeveloper.tiktok
+ Client
+ 1.0.14-Release
+ compile
+
+
+ net.bramp.ffmpeg
+ ffmpeg
+ 0.8.0
+ compile
+
+
+ io.github.jwdeveloper.tiktok
+ API
+ 1.0.17-Release
+ compile
+
+
+
+
+ 16
+ 16
+ UTF-8
+
+
+
\ No newline at end of file
diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/TikTokLiveRecorder.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/TikTokLiveRecorder.java
new file mode 100644
index 00000000..ca0ad623
--- /dev/null
+++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/TikTokLiveRecorder.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.extension.recorder;
+
+import io.github.jwdeveloper.tiktok.extension.recorder.api.LiveRecorder;
+import io.github.jwdeveloper.tiktok.extension.recorder.impl.RecorderListener;
+import io.github.jwdeveloper.tiktok.extension.recorder.impl.data.RecorderSettings;
+
+import java.util.function.Consumer;
+
+public class TikTokLiveRecorder
+{
+
+ public static LiveRecorder use(Consumer consumer)
+ {
+ return new RecorderListener(consumer);
+ }
+
+ public static LiveRecorder use()
+ {
+ return use(x ->{});
+ }
+}
diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/api/LiveRecorder.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/api/LiveRecorder.java
new file mode 100644
index 00000000..3aa4328a
--- /dev/null
+++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/api/LiveRecorder.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.extension.recorder.api;
+
+import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
+
+public interface LiveRecorder extends TikTokEventListener {
+
+ void pause();
+ void unpause();
+}
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
new file mode 100644
index 00000000..d283c480
--- /dev/null
+++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.extension.recorder.impl;
+
+import com.google.gson.JsonParser;
+import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver;
+import io.github.jwdeveloper.tiktok.data.events.TikTokLiveEndedEvent;
+import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
+import io.github.jwdeveloper.tiktok.extension.recorder.api.LiveRecorder;
+import io.github.jwdeveloper.tiktok.data.events.TikTokConnectedEvent;
+import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
+import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
+import io.github.jwdeveloper.tiktok.extension.recorder.impl.data.DownloadData;
+import io.github.jwdeveloper.tiktok.extension.recorder.impl.data.RecorderSettings;
+import io.github.jwdeveloper.tiktok.data.events.http.TikTokRoomDataResponseEvent;
+import io.github.jwdeveloper.tiktok.extension.recorder.impl.enums.LiveQuality;
+import io.github.jwdeveloper.tiktok.extension.recorder.impl.event.TikTokLiveRecorderStartedEvent;
+import io.github.jwdeveloper.tiktok.http.HttpClientFactory;
+import io.github.jwdeveloper.tiktok.live.LiveClient;
+import net.bramp.ffmpeg.FFmpeg;
+import net.bramp.ffmpeg.FFmpegExecutor;
+import net.bramp.ffmpeg.RunProcessFunction;
+import net.bramp.ffmpeg.builder.FFmpegBuilder;
+
+import javax.net.ssl.HttpsURLConnection;
+import java.io.*;
+import java.net.URL;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class RecorderListener implements LiveRecorder {
+
+ private final Consumer consumer;
+ private RecorderSettings settings;
+ private DownloadData downloadData;
+ private Thread liveDownloadThread;
+
+ public RecorderListener(Consumer consumer) {
+ this.consumer = consumer;
+ }
+
+ @Override
+ public void pause() {
+
+ }
+
+ @Override
+ public void unpause() {
+
+ }
+
+ @TikTokEventObserver
+ private void onResponse(LiveClient liveClient, TikTokRoomDataResponseEvent event) {
+ settings = RecorderSettings.DEFAULT();
+ consumer.accept(settings);
+
+ var json = event.getLiveData().getJson();
+
+ liveClient.getLogger().info("Searching for live download url");
+ if (settings.getPrepareDownloadData() != null) {
+ downloadData = settings.getPrepareDownloadData().apply(json);
+ } else {
+ downloadData = mapToDownloadData(json);
+ }
+
+ if (downloadData.getDownloadLiveUrl().isEmpty()) {
+ throw new TikTokLiveException("Unable to find download live url!");
+ }
+ liveClient.getLogger().info("Live download url found!");
+
+ }
+
+ @TikTokEventObserver
+ private void onConnected(LiveClient liveClient, TikTokConnectedEvent event) {
+ /* liveDownloadThread = new Thread(() ->
+ {
+ try {
+ var ffmpeg = new FFmpeg(settings.getFfmpegPath(), new RunProcessFunction() {
+ @Override
+ public Process run(final List args) throws IOException {
+ var process = super.run(args);
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ terminateFfmpeg(process);
+ }, "FFmpeg process destroyer"));
+ return process;
+ }
+ });
+
+ var builder = new FFmpegBuilder()
+ .setInput(downloadData.getFullUrl())
+ .addOutput(settings.getOutputPath() + File.separator + settings.getOutputFileName()) // Set the output file path
+ .setFormat("mp4")
+ .done();
+
+ var executor = new FFmpegExecutor(ffmpeg);
+ var ffmpegProcess = executor.createJob(builder, (progress)-> {
+ liveClient.getLogger().info("Downloading stream: " +progress.total_size);
+ });
+ ffmpegProcess.run();
+ liveClient.publishEvent(new TikTokLiveRecorderStartedEvent(downloadData));
+
+ } catch (Exception e) {
+ throw new TikTokLiveException("Unable to run ffmpeg drivers",e);
+ }
+ });
+*/
+
+ liveDownloadThread = new Thread(() ->
+ {
+ try {
+ var bufferSize = 1024;
+ var url = new URL(downloadData.getFullUrl());
+ HttpsURLConnection socksConnection = (HttpsURLConnection) url.openConnection();
+ var headers = LiveClientSettings.DefaultRequestHeaders();
+ for (var entry : headers.entrySet()) {
+ socksConnection.setRequestProperty(entry.getKey(), entry.getValue());
+ }
+
+ System.out.println(socksConnection.getResponseCode());
+ try (var in = new BufferedInputStream(socksConnection.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);
+ }
+ } catch (IOException e) {
+ throw e;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ ;
+ }
+
+ });
+
+
+ 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 = 0;
+ 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) {
+ liveDownloadThread.interrupt();
+ }
+
+ @TikTokEventObserver
+ private void onDisconnected(LiveClient liveClient, TikTokLiveEndedEvent event) {
+ liveDownloadThread.interrupt();
+ }
+
+ private int terminateFfmpeg(final Process process) {
+ if (!process.isAlive()) {
+ /*
+ * ffmpeg -version, do nothing
+ */
+ return process.exitValue();
+ }
+
+ /*
+ * ffmpeg -f x11grab
+ */
+ System.out.println("About to destroy the child process...");
+ try (final OutputStreamWriter out = new OutputStreamWriter(process.getOutputStream(), UTF_8)) {
+ out.write('q');
+ } catch (final IOException ioe) {
+ ioe.printStackTrace();
+ }
+ try {
+ if (!process.waitFor(5L, TimeUnit.SECONDS)) {
+ process.destroy();
+ process.waitFor();
+ }
+ return process.exitValue();
+ } catch (final InterruptedException ie) {
+ System.out.println("Interrupted");
+ ie.printStackTrace();
+ Thread.currentThread().interrupt();
+ return -1;
+ }
+ }
+
+ private DownloadData mapToDownloadData(String json) {
+
+ var parsedJson = JsonParser.parseString(json);
+ var jsonObject = parsedJson.getAsJsonObject();
+ var streamDataJson = jsonObject.getAsJsonObject("data")
+ .getAsJsonObject("stream_url")
+ .getAsJsonObject("live_core_sdk_data")
+ .getAsJsonObject("pull_data")
+ .get("stream_data")
+ .getAsString();
+
+ var streamDataJsonObject = JsonParser.parseString(streamDataJson).getAsJsonObject();
+
+ var urlLink = streamDataJsonObject.getAsJsonObject("data")
+ .getAsJsonObject(LiveQuality.origin.name())
+ .getAsJsonObject("main")
+ .get("flv")
+ .getAsString();
+
+
+ var sessionId = streamDataJsonObject.getAsJsonObject("common")
+ .get("session_id")
+ .getAsString();
+
+
+ //main
+ //https://pull-f5-tt03.fcdn.eu.tiktokcdn.com/stage/stream-3284937501738533765.flv?session_id=136-20240109000954BF818F1B3A8E5E39E238&_webnoredir=1
+ //Working
+ //https://pull-f5-tt03.fcdn.eu.tiktokcdn.com/game/stream-3284937501738533765_sd5.flv?_session_id=136-20240109001052D91FDBC00143211020C8.1704759052997&_webnoredir=1
+ //https://pull-f5-tt02.fcdn.eu.tiktokcdn.com/stage/stream-3861399216374940610_uhd5.flv?_session_id=136-20240109000223D0BAA1A83974490EE630.1704758544391&_webnoredir=1
+
+ return new DownloadData(urlLink, sessionId);
+ }
+
+
+}
diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/data/DownloadData.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/data/DownloadData.java
new file mode 100644
index 00000000..0bc02601
--- /dev/null
+++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/data/DownloadData.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.extension.recorder.impl.data;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class DownloadData {
+
+ private String downloadLiveUrl;
+
+ private String sessionId;
+
+ public String getFullUrl() {
+ return downloadLiveUrl + "?_webnoredir=1&session_id=" + sessionId;
+ }
+}
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
new file mode 100644
index 00000000..ea4a8ec4
--- /dev/null
+++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/data/RecorderSettings.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package 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.LiveFormat;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.function.Function;
+
+/**
+ * self,
+ * path: str,
+ * duration: Optional[int] = None,
+ * quality: Optional[VideoQuality] = None,
+ * verbose: bool = True,
+ * loglevel: str = "error",
+ * global_options: Set[str] = set(),
+ * inputs: Dict[str, str] = dict(),
+ * outputs: Dict[str, str] = dict()
+ * :param loglevel: Set the FFmpeg log level
+ * :param outputs: Pass custom params to FFmpeg outputs
+ * :param inputs: Pass custom params to FFmpeg inputs
+ * :param global_options: Pass custom params to FFmpeg global options
+ * :param path: The path to download the livestream video to
+ * :param duration: If duration is None or less than 1, download will go forever
+ * :param quality: If quality is None, download quality will auto
+ * :param verbose: Whether to log info about the download in console
+ */
+@Getter
+@Setter
+public class RecorderSettings {
+ private String ffmpegPath;
+ private String quality;
+ private String format;
+ private String outputPath;
+ private String outputFileName;
+ private Function prepareDownloadData;
+ private boolean startOnConnected;
+
+ public static RecorderSettings DEFAULT() {
+ return new RecorderSettings();
+ }
+
+ public void setQuality(String format) {
+ this.format = format;
+ }
+
+ public void setQuality(LiveQuality quality) {
+ this.quality = quality.name();
+ }
+
+
+ public void setFormat(String format) {
+ this.format = format;
+ }
+
+ public void setFormat(LiveFormat format) {
+ this.format = format.name();
+ }
+}
diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/enums/LiveFormat.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/enums/LiveFormat.java
new file mode 100644
index 00000000..e48c83c0
--- /dev/null
+++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/enums/LiveFormat.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.extension.recorder.impl.enums;
+
+public enum LiveFormat
+{
+ MP4
+}
diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/enums/LiveQuality.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/enums/LiveQuality.java
new file mode 100644
index 00000000..9171a75e
--- /dev/null
+++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/enums/LiveQuality.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.extension.recorder.impl.enums;
+
+public enum LiveQuality {
+ origin, hd_60, ao, hd, sd, ld,uhd_60
+}
diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/event/TikTokLiveRecorderStartedEvent.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/event/TikTokLiveRecorderStartedEvent.java
new file mode 100644
index 00000000..99914a8a
--- /dev/null
+++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/event/TikTokLiveRecorderStartedEvent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package io.github.jwdeveloper.tiktok.extension.recorder.impl.event;
+
+import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
+import io.github.jwdeveloper.tiktok.extension.recorder.impl.data.DownloadData;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@AllArgsConstructor
+@Data
+public class TikTokLiveRecorderStartedEvent extends TikTokEvent
+{
+ DownloadData downloadData;
+}
diff --git a/pom.xml b/pom.xml
index 74df4121..9a96d50a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,6 +16,9 @@
Tools-EventsCollector
Tools-ReadmeGenerator
Tools-EventsWebViewer
+
+ extension-recorder
+ extension-collector