Compare commits

...

25 Commits

Author SHA1 Message Date
David Kohler
5794ff2a57 MINOR: Merge pull request #56 from kohlerpop1/fixes-updates
MINOR: Switched to new Signing Server endpoint and more
2024-02-15 13:53:36 -05:00
kohlerpop1
d471e87dd7 Converted magic number to constant AGE_RESTRICTED_CODE 2024-02-15 12:55:45 -05:00
kohlerpop1
c89bcad894 Removed System.out.println of response headers! 2024-02-15 12:13:52 -05:00
kohlerpop1
c9a84c39df Merge remote-tracking branch 'origin/fixes-updates' into fixes-updates 2024-02-15 12:10:52 -05:00
kohlerpop1
c1105f1324 Switched to new Signing Server endpoint and more 2024-02-15 12:10:17 -05:00
kohlerpop1
243ce9bc94 Added PreConnectionEvent with LiveType, made optimizations, and added fallback to default request in proxy class in case proxy protocol is not supported by TikTok or Signing server. 2024-02-15 11:46:13 -05:00
GitHub Action
4f141edb1a Update version in pom.xml 2024-02-15 00:26:07 +00:00
Jacek W
359a1508c7 MINOR 2024-02-15 01:24:26 +01:00
Jacek W
bbfa7b410b Merge pull request #55 from jwdeveloper/develop-1.2.0
Develop 1.2.0
2024-02-15 01:23:56 +01:00
GitHub Action
6da40927d0 Update version in pom.xml 2024-02-15 00:18:41 +00:00
Jacek W
4d97fd9157 Merge pull request #54 from kohlerpop1/fixes-updates
MINOR: Added PreConnectionEvent with LiveType, made optimizations, and more
2024-02-15 01:16:59 +01:00
kohlerpop1
1ba51476d1 Added PreConnectionEvent with LiveType, made optimizations, and added fallback to default request in proxy class in case proxy protocol is not supported by TikTok or Signing server. 2024-02-12 15:24:54 -05:00
GitHub Action
9ee5c89f64 Update version in pom.xml 2024-02-11 11:40:17 +00:00
Jacek W
ffabf098c0 Merge pull request #53 from jwdeveloper/develop-1.1.1
Develop 1.1.1
2024-02-11 12:38:40 +01:00
David Kohler
7468fc2385 Merge pull request #52 from kohlerpop1/fixes-updates
Changed ? to & as TikTok now sends urls with defined parameters already
2024-02-09 22:36:08 -05:00
kohlerpop1
abbb557881 Changed ? to & as TikTok now sends urls with defined parameters already 2024-02-09 22:28:34 -05:00
Jacek W
19c513afe6 Merge pull request #50 from kohlerpop1/fixes-updates
Fixed bug, added final, removed not needed initialization, and minor improvements.
2024-01-22 23:26:43 +01:00
kohlerpop1
283024a1d4 Fixed NPE - Caused by: java.lang.NullPointerException: Cannot invoke "java.lang.Thread.interrupt()" because "this.liveDownloadThread" is null
Moved websocketClient.setSocketFactory call up where it only needs called once not looped. Also added pingingTask.run for not used proxy connections.
2024-01-21 13:00:49 -05:00
kohlerpop1
a0ac9e6d79 Fixed bug, added final, removed not needed initialization, and minor improvements. 2024-01-19 16:45:35 -05:00
Jacek W
2385d1e75e Update README.md 2024-01-19 19:35:24 +01:00
Jacek W
c9c7f62d4a Update README.md 2024-01-19 19:34:39 +01:00
Jacek W
35ef95096d Update README.md 2024-01-19 19:32:48 +01:00
JW
46bcfd6eb8 Merge remote-tracking branch 'origin/master' 2024-01-19 19:31:36 +01:00
JW
38b66395cb Fixed poms files 2024-01-19 19:31:30 +01:00
GitHub Action
46e75dec1a Update version in pom.xml 2024-01-19 18:19:23 +00:00
36 changed files with 790 additions and 643 deletions

View File

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

View File

@@ -20,16 +20,25 @@
* 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.requests;
package io.github.jwdeveloper.tiktok.data.events.control;
import lombok.AllArgsConstructor;
import lombok.Data;
import io.github.jwdeveloper.tiktok.annotations.*;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokLiveClientEvent;
import io.github.jwdeveloper.tiktok.data.requests.*;
import lombok.*;
@Data
@AllArgsConstructor
public class SignServerResponse
/**
* Triggered before the connection is established.
*/
@EventMeta(eventType = EventType.Control)
public class TikTokPreConnectionEvent extends TikTokLiveClientEvent
{
private String signedUrl;
@Getter private final LiveUserData.Response userData;
@Getter private final LiveData.Response roomData;
@Getter @Setter boolean cancelConnection = false;
private String userAgent;
}
public TikTokPreConnectionEvent(LiveUserData.Response userData, LiveData.Response liveData) {
this.userData = userData;
this.roomData = liveData;
}
}

View File

@@ -44,6 +44,7 @@ public class LiveData {
private int totalViewers;
private boolean ageRestricted;
private User host;
private LiveType liveType;
}
public enum LiveStatus {
@@ -51,4 +52,11 @@ public class LiveData {
HostOnline,
HostOffline,
}
}
public enum LiveType {
SOLO,
BOX,
BATTLE,
CO_HOST
}
}

View File

@@ -36,7 +36,6 @@ public class LiveClientSettings {
/**
* ISO-Language for Client
*/
private String clientLanguage;
/**
@@ -44,7 +43,6 @@ public class LiveClientSettings {
*/
private boolean retryOnConnectionFailure;
/**
* Before retrying connect, wait for select amount of time
*/
@@ -53,41 +51,39 @@ public class LiveClientSettings {
/**
* Whether to print Logs to Console
*/
private boolean printToConsole = true;
/**
* LoggingLevel for Logs
*/
private Level logLevel;
/**
* Optional: Use it if you need to change TikTok live hostname in builder
*/
private String hostName;
/**
* Parameters used in requests to TikTok api
*/
private HttpClientSettings httpSettings;
/*
/**
* Optional: Sometimes not every messages from chat are send to TikTokLiveJava to fix this issue you can set sessionId
* documentation how to obtain sessionId https://github.com/isaackogan/TikTok-Live-Connector#send-chat-messages
*/
private String sessionId;
/*
/**
* Optional: By default roomID is fetched before connect to live, but you can set it manually
*
*/
private String roomId;
/**
* Optional: API Key for increased limit to signing server
*/
private String apiKey;
public static LiveClientSettings createDefault()
{
@@ -103,12 +99,10 @@ public class LiveClientSettings {
clientSettings.setPrintToConsole(false);
clientSettings.setLogLevel(Level.ALL);
clientSettings.setHttpSettings(httpSettings);
return clientSettings;
}
/**
* Default Parameters for HTTP-Request
*/
@@ -147,11 +141,9 @@ public class LiveClientSettings {
clientParams.put("webcast_sdk_version", "1.3.0");
clientParams.put("update_version_code", "1.3.0");
return clientParams;
}
/**
* Default Headers for HTTP-Request
*/
@@ -167,6 +159,4 @@ public class LiveClientSettings {
headers.put("Accept-Language", "en-US,en; q=0.9");
return headers;
}
}
}

View File

@@ -33,11 +33,10 @@ import java.util.function.Consumer;
@Setter
public class ProxyClientSettings implements Iterator<ProxyData>
{
private boolean enabled, lastSuccess;
private boolean enabled, autoDiscard = true, fallback = true;
private Rotation rotation = Rotation.CONSECUTIVE;
private final List<ProxyData> proxyList = new ArrayList<>();
private int index = 0;
private boolean autoDiscard = true;
private int index = -1;
private Proxy.Type type = Proxy.Type.DIRECT;
private Consumer<ProxyData> onProxyUpdated = x -> {};
@@ -64,10 +63,6 @@ public class ProxyClientSettings implements Iterator<ProxyData>
@Override
public ProxyData next() {
return lastSuccess ? proxyList.get(index) : rotate();
}
public ProxyData rotate() {
var nextProxy = switch (rotation)
{
case CONSECUTIVE -> {
@@ -78,11 +73,14 @@ public class ProxyClientSettings implements Iterator<ProxyData>
index = new Random().nextInt(proxyList.size());
yield proxyList.get(index).clone();
}
case NONE -> proxyList.get(index).clone();
case NONE -> {
index = Math.max(index, 0);
yield proxyList.get(index).clone();
}
};
onProxyUpdated.accept(nextProxy);
return nextProxy;
}
}
@Override
public void remove() {
@@ -98,6 +96,7 @@ public class ProxyClientSettings implements Iterator<ProxyData>
this.index = index;
}
}
public ProxyClientSettings clone()
{
ProxyClientSettings settings = new ProxyClientSettings();

View File

@@ -23,7 +23,7 @@
package io.github.jwdeveloper.tiktok.exceptions;
/*
/**
* Happens while bad response from Http request to TikTok
*/
public class TikTokLiveRequestException extends TikTokLiveException
@@ -46,4 +46,4 @@ public class TikTokLiveRequestException extends TikTokLiveException
public TikTokLiveRequestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
}

View File

@@ -27,9 +27,8 @@ import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
public interface LiveHttpClient {
public interface LiveHttpClient
{
/**
* @return list of gifts that are available in your country
*/
@@ -37,28 +36,32 @@ public interface LiveHttpClient {
/**
* Returns information about user that is having a livestream
*
* @param userName
* @return
* @param userName name of user
* @return {@link LiveUserData.Response}
*/
LiveUserData.Response fetchLiveUserData(String userName);
default LiveUserData.Response fetchLiveUserData(String userName) {
return fetchLiveUserData(new LiveUserData.Request(userName));
}
LiveUserData.Response fetchLiveUserData(LiveUserData.Request request);
/**
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
* @return
* @return {@link LiveData.Response}
*/
LiveData.Response fetchLiveData(String roomId);
default LiveData.Response fetchLiveData(String roomId) {
return fetchLiveData(new LiveData.Request(roomId));
}
LiveData.Response fetchLiveData(LiveData.Request request);
/**
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
* @return
* @return {@link LiveConnectionData.Response}
*/
LiveConnectionData.Response fetchLiveConnectionData(String roomId);
default LiveConnectionData.Response fetchLiveConnectionData(String roomId) {
return fetchLiveConnectionData(new LiveConnectionData.Request(roomId));
}
LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request);
}
}

View File

@@ -24,6 +24,7 @@ package io.github.jwdeveloper.tiktok.live.builder;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.data.events.control.TikTokPreConnectionEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
import io.github.jwdeveloper.tiktok.data.events.http.TikTokHttpResponseEvent;
@@ -149,6 +150,13 @@ public interface EventsBuilder<T> {
*/
T onConnected(EventConsumer<TikTokConnectedEvent> action);
/**
* Invoked before client has been successfully connected to live
* @param action
* @return
*/
T onPreConnection(EventConsumer<TikTokPreConnectionEvent> action);
/**
* Invoked when client tries to reconnect
* @param action
@@ -215,6 +223,4 @@ public interface EventsBuilder<T> {
//T onLinkMicBattle(TikTokEventConsumer<TikTokLinkMicBattleEvent> event);
//T onUnhandledControl(TikTokEventConsumer<TikTokUnhandledControlEvent> event);
}
}

View File

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

View File

@@ -26,14 +26,13 @@ import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
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.control.*;
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;
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
import io.github.jwdeveloper.tiktok.exceptions.*;
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
import io.github.jwdeveloper.tiktok.listener.ListenersManager;
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
@@ -127,22 +126,26 @@ public class TikTokLiveClient implements LiveClient {
var userData = httpClient.fetchLiveUserData(userDataRequest);
liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp());
liveRoomInfo.setRoomId(userData.getRoomId());
if (userData.getUserStatus() == LiveUserData.UserStatus.Offline) {
throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostUser());
}
if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound) {
throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostUser());
}
if (userData.getUserStatus() == LiveUserData.UserStatus.Offline)
throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostName());
if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound)
throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostName());
var liveDataRequest = new LiveData.Request(userData.getRoomId());
var liveData = httpClient.fetchLiveData(liveDataRequest);
if (liveData.isAgeRestricted())
throw new TikTokLiveException("Livestream for "+liveRoomInfo.getHostName()+" is 18+ or age restricted!");
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound)
throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" could not be found.");
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline)
throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" not found, is the Host offline?");
tikTokEventHandler.publish(this, new TikTokRoomDataResponseEvent(liveData));
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) {
throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found.");
}
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline) {
throw new TikTokLiveOfflineHostException("LiveStream for not be found, is the Host offline?");
}
liveRoomInfo.setTitle(liveData.getTitle());
liveRoomInfo.setViewersCount(liveData.getViewers());
@@ -150,6 +153,11 @@ public class TikTokLiveClient implements LiveClient {
liveRoomInfo.setAgeRestricted(liveData.isAgeRestricted());
liveRoomInfo.setHost(liveData.getHost());
var preconnectEvent = new TikTokPreConnectionEvent(userData, liveData);
tikTokEventHandler.publish(this, preconnectEvent);
if (preconnectEvent.isCancelConnection())
throw new TikTokLiveException("TikTokPreConnectionEvent cancelled connection!");
var liveConnectionRequest =new LiveConnectionData.Request(userData.getRoomId());
var liveConnectionData = httpClient.fetchLiveConnectionData(liveConnectionRequest);
webSocketClient.start(liveConnectionData, this);

View File

@@ -24,6 +24,7 @@ package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.control.TikTokPreConnectionEvent;
import io.github.jwdeveloper.tiktok.data.events.envelop.TikTokChestEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftComboEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
@@ -98,7 +99,8 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
}
public TikTokLiveClientBuilder addListener(TikTokEventListener listener) {
listeners.add(listener);
if (listener != null)
listeners.add(listener);
return this;
}
@@ -275,9 +277,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return build().connectAsync();
}
public TikTokLiveClientBuilder onUnhandledSocial(
EventConsumer<TikTokUnhandledSocialEvent> event) {
public TikTokLiveClientBuilder onUnhandledSocial(EventConsumer<TikTokUnhandledSocialEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledSocialEvent.class, event);
return this;
}
@@ -289,8 +289,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
}
public TikTokLiveClientBuilder onLinkMicFanTicket(
EventConsumer<TikTokLinkMicFanTicketEvent> event) {
public TikTokLiveClientBuilder onLinkMicFanTicket(EventConsumer<TikTokLinkMicFanTicketEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMicFanTicketEvent.class, event);
return this;
}
@@ -305,14 +304,12 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this;
}
public TikTokLiveClientBuilder onDetect(
EventConsumer<TikTokDetectEvent> event) {
public TikTokLiveClientBuilder onDetect(EventConsumer<TikTokDetectEvent> event) {
tikTokEventHandler.subscribe(TikTokDetectEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onLinkLayer(
EventConsumer<TikTokLinkLayerEvent> event) {
public TikTokLiveClientBuilder onLinkLayer(EventConsumer<TikTokLinkLayerEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkLayerEvent.class, event);
return this;
}
@@ -322,6 +319,11 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this;
}
public TikTokLiveClientBuilder onPreConnection(EventConsumer<TikTokPreConnectionEvent> event) {
tikTokEventHandler.subscribe(TikTokPreConnectionEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onCaption(EventConsumer<TikTokCaptionEvent> event) {
tikTokEventHandler.subscribe(TikTokCaptionEvent.class, event);
return this;
@@ -332,8 +334,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this;
}
public TikTokLiveClientBuilder onRoomPin(
EventConsumer<TikTokRoomPinEvent> event) {
public TikTokLiveClientBuilder onRoomPin(EventConsumer<TikTokRoomPinEvent> event) {
tikTokEventHandler.subscribe(TikTokRoomPinEvent.class, event);
return this;
}
@@ -373,8 +374,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this;
}
public TikTokLiveClientBuilder onBarrage(
EventConsumer<TikTokBarrageEvent> event) {
public TikTokLiveClientBuilder onBarrage(EventConsumer<TikTokBarrageEvent> event) {
tikTokEventHandler.subscribe(TikTokBarrageEvent.class, event);
return this;
}
@@ -391,8 +391,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
}
public TikTokLiveClientBuilder onLinkMicArmies(
EventConsumer<TikTokLinkMicArmiesEvent> event) {
public TikTokLiveClientBuilder onLinkMicArmies(EventConsumer<TikTokLinkMicArmiesEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMicArmiesEvent.class, event);
return this;
}
@@ -402,20 +401,17 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this;
}
public TikTokLiveClientBuilder onUnauthorizedMember(
EventConsumer<TikTokUnauthorizedMemberEvent> event) {
public TikTokLiveClientBuilder onUnauthorizedMember(EventConsumer<TikTokUnauthorizedMemberEvent> event) {
tikTokEventHandler.subscribe(TikTokUnauthorizedMemberEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onInRoomBanner(
EventConsumer<TikTokInRoomBannerEvent> event) {
public TikTokLiveClientBuilder onInRoomBanner(EventConsumer<TikTokInRoomBannerEvent> event) {
tikTokEventHandler.subscribe(TikTokInRoomBannerEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onLinkMicMethod(
EventConsumer<TikTokLinkMicMethodEvent> event) {
public TikTokLiveClientBuilder onLinkMicMethod(EventConsumer<TikTokLinkMicMethodEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMicMethodEvent.class, event);
return this;
}
@@ -487,8 +483,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this;
}
public TikTokLiveClientBuilder onUnhandledMember(
EventConsumer<TikTokUnhandledMemberEvent> event) {
public TikTokLiveClientBuilder onUnhandledMember(EventConsumer<TikTokUnhandledMemberEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledMemberEvent.class, event);
return this;
}
@@ -498,20 +493,17 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
return this;
}
public TikTokLiveClientBuilder onLinkMicBattle(
EventConsumer<TikTokLinkMicBattleEvent> event) {
public TikTokLiveClientBuilder onLinkMicBattle(EventConsumer<TikTokLinkMicBattleEvent> event) {
tikTokEventHandler.subscribe(TikTokLinkMicBattleEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onDisconnected(
EventConsumer<TikTokDisconnectedEvent> event) {
public TikTokLiveClientBuilder onDisconnected(EventConsumer<TikTokDisconnectedEvent> event) {
tikTokEventHandler.subscribe(TikTokDisconnectedEvent.class, event);
return this;
}
public TikTokLiveClientBuilder onUnhandledControl(
EventConsumer<TikTokUnhandledControlEvent> event) {
public TikTokLiveClientBuilder onUnhandledControl(EventConsumer<TikTokUnhandledControlEvent> event) {
tikTokEventHandler.subscribe(TikTokUnhandledControlEvent.class, event);
return this;
}

View File

@@ -36,18 +36,17 @@ import java.util.Optional;
public class TikTokLiveHttpClient implements LiveHttpClient {
/**
* Signing API by Isaac Kogan
* https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures
*/
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/sign_url";
* <a href="https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures">Signing API by Isaac Kogan</a>
*/
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch";
private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/";
private static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/";
public static final int TIKTOK_AGE_RESTRICTED_CODE = 4003110;
private final HttpClientFactory httpFactory;
private final LiveClientSettings clientSettings;
private final LiveUserDataMapper liveUserDataMapper;
private final LiveDataMapper liveDataMapper;
private final SignServerResponseMapper signServerResponseMapper;
private final GiftsDataMapper giftsDataMapper;
public TikTokLiveHttpClient(HttpClientFactory factory, LiveClientSettings settings) {
@@ -55,7 +54,6 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
clientSettings = settings;
liveUserDataMapper = new LiveUserDataMapper();
liveDataMapper = new LiveDataMapper();
signServerResponseMapper = new SignServerResponseMapper();
giftsDataMapper = new GiftsDataMapper();
}
@@ -66,6 +64,22 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
public GiftsData.Response fetchGiftsData() {
var url = TIKTOK_URL_WEBCAST + "gift/list/";
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
if (proxyClientSettings.isEnabled()) {
while (proxyClientSettings.hasNext()) {
try {
var optional = httpFactory.client(url)
.build()
.toJsonResponse();
if (optional.isEmpty()) {
throw new TikTokLiveRequestException("Unable to fetch gifts information's");
}
var json = optional.get();
return giftsDataMapper.map(json);
} catch (TikTokProxyRequestException ignored) {}
}
}
var optional = httpFactory.client(url)
.build()
.toJsonResponse();
@@ -73,23 +87,18 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
if (optional.isEmpty()) {
throw new TikTokLiveRequestException("Unable to fetch gifts information's");
}
var json = optional.get();
return giftsDataMapper.map(json);
}
@Override
public LiveUserData.Response fetchLiveUserData(String userName) {
return fetchLiveUserData(new LiveUserData.Request(userName));
}
@Override
public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) {
var url = TIKTOK_URL_WEB + "api-live/user/room";
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
if (proxyClientSettings.isEnabled()) {
while (proxyClientSettings.hasNext()) {
try {
var url = TIKTOK_URL_WEB + "api-live/user/room";
var optional = httpFactory.client(url)
.withParam("uniqueId", request.getUserName())
.withParam("sourceType", "54")
@@ -105,7 +114,6 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
} catch (TikTokProxyRequestException ignored) {}
}
}
var url = TIKTOK_URL_WEB + "api-live/user/room";
var optional = httpFactory.client(url)
.withParam("uniqueId", request.getUserName())
.withParam("sourceType", "54")
@@ -120,18 +128,13 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
return liveUserDataMapper.map(json);
}
@Override
public LiveData.Response fetchLiveData(String roomId) {
return fetchLiveData(new LiveData.Request(roomId));
}
@Override
public LiveData.Response fetchLiveData(LiveData.Request request) {
var url = TIKTOK_URL_WEBCAST + "room/info";
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
if (proxyClientSettings.isEnabled()) {
while (proxyClientSettings.hasNext()) {
try {
var url = TIKTOK_URL_WEBCAST + "room/info";
var optional = httpFactory.client(url)
.withParam("room_id", request.getRoomId())
.build()
@@ -146,7 +149,6 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
} catch (TikTokProxyRequestException ignored) {}
}
}
var url = TIKTOK_URL_WEBCAST + "room/info";
var optional = httpFactory.client(url)
.withParam("room_id", request.getRoomId())
.build()
@@ -160,20 +162,12 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
return liveDataMapper.map(json);
}
@Override
public LiveConnectionData.Response fetchLiveConnectionData(String roomId) {
return fetchLiveConnectionData(new LiveConnectionData.Request(roomId));
}
@Override
public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) {
HttpResponse<byte[]> credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> {
SignServerResponse signServerResponse = getSignedUrl(request.getRoomId());
return getWebsocketCredentialsResponse(signServerResponse.getSignedUrl());
});
HttpResponse<byte[]> credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> getStarterPayload(request.getRoomId()));
try {
var optionalHeader = credentialsResponse.headers().firstValue("set-cookie");
var optionalHeader = credentialsResponse.headers().firstValue("x-set-tt-cookie");
if (optionalHeader.isEmpty()) {
throw new TikTokSignServerException("Sign server did not return the set-cookie header");
}
@@ -195,39 +189,21 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
}
}
SignServerResponse getSignedUrl(String roomId) {
var urlToSign = httpFactory
.client(TikTokLiveHttpClient.TIKTOK_URL_WEBCAST + "im/fetch")
.withParam("room_id", roomId)
.build()
.toUrl();
HttpResponse<byte[]> getStarterPayload(String room_id) {
HttpClientBuilder builder = httpFactory.client(TIKTOK_SIGN_API)
.withParam("client", "ttlive-java")
.withParam("uuc", "1")
.withParam("room_id", room_id);
if (clientSettings.getApiKey() != null)
builder.withParam("apiKey", clientSettings.getApiKey());
var optional = httpFactory
.client(TikTokLiveHttpClient.TIKTOK_SIGN_API)
.withParam("client", "ttlive-java")
.withParam("uuc", "1")
.withParam("url", urlToSign.toString())
.build()
.toJsonResponse();
var optional = builder.build().toResponse();
if (optional.isEmpty()) {
throw new TikTokSignServerException("Unable to sign url: " + urlToSign);
}
var json = optional.get();
return signServerResponseMapper.map(json);
}
HttpResponse<byte[]> getWebsocketCredentialsResponse(String signedUrl) {
var optionalResponse = httpFactory
.clientEmpty(signedUrl)
.build()
.toResponse();
if (optionalResponse.isEmpty()) {
throw new TikTokSignServerException("Unable to get websocket connection credentials");
}
return optionalResponse.get();
return optional.get();
}
Optional<HttpResponse<byte[]>> getOptionalProxyResponse(LiveConnectionData.Request request) {
@@ -235,9 +211,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
if (proxyClientSettings.isEnabled()) {
while (proxyClientSettings.hasNext()) {
try {
SignServerResponse signServerResponse = getSignedUrl(request.getRoomId());
HttpResponse<byte[]> credentialsResponse = getWebsocketCredentialsResponse(signServerResponse.getSignedUrl());
clientSettings.getHttpSettings().getProxyClientSettings().rotate();
HttpResponse<byte[]> credentialsResponse = getStarterPayload(request.getRoomId());
return Optional.of(credentialsResponse);
} catch (TikTokProxyRequestException | TikTokSignServerException ignored) {}
}

View File

@@ -39,7 +39,7 @@ public class HttpClient {
protected final String url;
private final Pattern pattern = Pattern.compile("charset=(.*?)(?=&|$)");
public Optional<HttpResponse<byte[]>> toResponse() {
public Optional<HttpResponse<byte[]>> toResponse() {
var client = prepareClient();
var request = prepareGetRequest();
try {

View File

@@ -67,16 +67,17 @@ public class HttpProxyClient extends HttpClient
var request = prepareGetRequest();
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() != 200) {
proxySettings.setLastSuccess(false);
if (response.statusCode() != 200)
continue;
}
proxySettings.setLastSuccess(true);
return Optional.of(response);
} catch (HttpConnectTimeoutException | ConnectException e) {
if (proxySettings.isAutoDiscard())
proxySettings.remove();
proxySettings.setLastSuccess(false);
throw new TikTokProxyRequestException(e);
} catch (IOException e) {
if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported
return super.toResponse();
throw new TikTokProxyRequestException(e);
} catch (Exception e) {
throw new TikTokLiveRequestException(e);
}
@@ -116,12 +117,12 @@ public class HttpProxyClient extends HttpClient
var response = createHttpResponse(body, toUrl(), responseInfo);
proxySettings.setLastSuccess(true);
return Optional.of(response);
} catch (IOException e) {
if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported
return super.toResponse();
if (proxySettings.isAutoDiscard())
proxySettings.remove();
proxySettings.setLastSuccess(false);
throw new TikTokProxyRequestException(e);
} catch (Exception e) {
throw new TikTokLiveRequestException(e);
@@ -130,7 +131,7 @@ public class HttpProxyClient extends HttpClient
throw new TikTokLiveRequestException("No more proxies available!");
} catch (NoSuchAlgorithmException | MalformedURLException | KeyManagementException e) {
// Should never be reached!
System.out.println("handleSocksProxyRequest()! If you see this message, reach us on discord!");
System.out.println("handleSocksProxyRequest: If you see this, message us on discord!");
e.printStackTrace();
return Optional.empty();
} catch (TikTokLiveRequestException e) {

View File

@@ -24,6 +24,7 @@ package io.github.jwdeveloper.tiktok.http.mappers;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient;
import io.github.jwdeveloper.tiktok.data.models.Picture;
import io.github.jwdeveloper.tiktok.data.models.users.User;
import io.github.jwdeveloper.tiktok.data.models.users.UserAttribute;
@@ -64,6 +65,9 @@ public class LiveDataMapper {
default -> LiveData.LiveStatus.HostNotFound;
};
response.setLiveStatus(statusValue);
} else if (data.has("prompts") && jsonObject.has("status_code") &&
data.get("prompts").getAsString().isEmpty() && jsonObject.get("status_code").isJsonPrimitive()) {
response.setAgeRestricted(jsonObject.get("status_code").getAsInt() == TikTokLiveHttpClient.TIKTOK_AGE_RESTRICTED_CODE);
} else {
response.setLiveStatus(LiveData.LiveStatus.HostNotFound);
}
@@ -105,6 +109,22 @@ public class LiveDataMapper {
response.setHost(user);
}
if (data.has("link_mic")) {
var element = data.getAsJsonObject("link_mic");
var multi_live = element.get("multi_live_enum").getAsInt();
var rival_id = element.get("rival_anchor_id").getAsInt();
var battle_scores = element.get("battle_scores").getAsJsonArray();
if (multi_live == 1) {
if (!battle_scores.isEmpty())
response.setLiveType(LiveData.LiveType.BATTLE);
else if (rival_id != 0)
response.setLiveType(LiveData.LiveType.CO_HOST);
else
response.setLiveType(LiveData.LiveType.BOX);
} else
response.setLiveType(LiveData.LiveType.SOLO);
}
return response;
}
@@ -127,4 +147,4 @@ public class LiveDataMapper {
user.addAttribute(UserAttribute.LiveHost);
return user;
}
}
}

View File

@@ -1,37 +0,0 @@
/*
* 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.http.mappers;
import com.google.gson.JsonParser;
import io.github.jwdeveloper.tiktok.data.requests.SignServerResponse;
public class SignServerResponseMapper {
public SignServerResponse map(String json) {
var parsedJson = JsonParser.parseString(json);
var jsonObject = parsedJson.getAsJsonObject();
var signUrl = jsonObject.get("signedUrl").getAsString();
var userAgent = jsonObject.get("User-Agent").getAsString();
return new SignServerResponse(signUrl, userAgent);
}
}

View File

@@ -26,7 +26,7 @@ import io.github.jwdeveloper.tiktok.*;
import io.github.jwdeveloper.tiktok.data.dto.ProxyData;
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
import io.github.jwdeveloper.tiktok.data.settings.*;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.exceptions.*;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import org.java_websocket.client.WebSocketClient;
@@ -41,7 +41,7 @@ public class TikTokWebSocketClient implements SocketClient {
private final TikTokLiveEventHandler tikTokEventHandler;
private WebSocketClient webSocketClient;
private TikTokWebSocketPingingTask pingingTask;
private final TikTokWebSocketPingingTask pingingTask;
private boolean isConnected;
public TikTokWebSocketClient(
@@ -91,6 +91,20 @@ public class TikTokWebSocketClient implements SocketClient {
}
public void connectProxy(ProxyClientSettings proxySettings) {
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; }
}}, null);
webSocketClient.setSocketFactory(sc.getSocketFactory());
}
} catch (Exception e) {
// This will never be thrown.
throw new TikTokProxyRequestException("Unable to set Socks proxy SSL instance");
}
while (proxySettings.hasNext()) {
ProxyData proxyData = proxySettings.next();
if (!tryProxyConnection(proxySettings, proxyData)) {
@@ -98,6 +112,7 @@ public class TikTokWebSocketClient implements SocketClient {
proxySettings.remove();
continue;
}
pingingTask.run(webSocketClient);
isConnected = true;
break;
}
@@ -107,21 +122,6 @@ public class TikTokWebSocketClient implements SocketClient {
public boolean tryProxyConnection(ProxyClientSettings proxySettings, ProxyData proxyData) {
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;
}
}}, null);
webSocketClient.setSocketFactory(sc.getSocketFactory());
}
webSocketClient.setProxy(new Proxy(proxySettings.getType(), proxyData.toSocketAddress()));
webSocketClient.connect();
return true;

View File

@@ -11,24 +11,18 @@ public class TikTokWebSocketPingingTask
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 = new Thread(() -> pingTask(webSocket));
isRunning = true;
thread.start();
}
public void stop()
{
if(thread != null)
{
if (thread != null)
thread.interrupt();
}
isRunning = false;
}
@@ -36,12 +30,9 @@ public class TikTokWebSocketPingingTask
private void pingTask(WebSocket webSocket)
{
var random = new Random();
while (isRunning)
{
try
{
if(!webSocket.isOpen())
{
while (isRunning) {
try {
if (!webSocket.isOpen()) {
Thread.sleep(100);
continue;
}
@@ -50,11 +41,10 @@ public class TikTokWebSocketPingingTask
var timeout = random.nextInt(MAX_TIMEOUT)+MIN_TIMEOUT;
Thread.sleep(timeout);
}
catch (Exception e)
{
catch (Exception e) {
isRunning = false;
}
}
}
}
}

View File

@@ -41,7 +41,7 @@
<parent>
<artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<version>1.0.17-Release</version>
<version>1.2.0-Release</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -75,7 +75,7 @@
<dependency>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>extension-collector</artifactId>
<version>1.0.17-Release</version>
<version>1.2.0-Release</version>
<scope>compile</scope>
</dependency>
</dependencies>

View File

@@ -49,7 +49,7 @@ public class CollectorExample {
collector.connectDatabase();
var users = List.of("tehila_723", "dino123597", "domaxyzx", "dash4214", "obserwacje_live");
var sessionTag = "Dupa";
var sessionTag = "Tag1";
for (var user : users) {
TikTokLive.newClient(user)
.configure(liveClientSettings ->

458
README.md
View File

@@ -29,8 +29,7 @@ A Java library inspired by [TikTokLive](https://github.com/isaackogan/TikTokLive
The library includes a wrapper that connects to the WebCast service using just the username (`uniqueId`). This allows you to connect to your own live chat as well as the live chat of other streamers.
No credentials are required. Events such as [Members Joining](#member), [Gifts](#gift), [Subscriptions](#subscribe), [Viewers](#roomuser), [Follows](#social), [Shares](#social), [Questions](#questionnew), [Likes](#like) and [Battles](#linkmicbattle) can be tracked.
# Contributors
[Library documentation for contributors](https://github.com/jwdeveloper/TikTokLiveJava/wiki)
<div align="center">
<a href="https://www.youtube.com/watch?v=eerWGgUKc6c" align="right" target="blank"><img src="https://img.youtube.com/vi/eerWGgUKc6c/hqdefault.jpg" alt="IMAGE ALT TEXT" width="38%" align="right"></a>
@@ -50,6 +49,7 @@ Do you prefer other programming languages?
#### Overview
- [Getting started](#getting-started)
- [Events](#events)
- [Extensions](#extensions)
- [Listeners](#listeners)
- [Contributing](#contributing)
@@ -70,7 +70,7 @@ Maven
<dependency>
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
<artifactId>Client</artifactId>
<version>1.0.16-Release</version>
<version>1.2.0-Release</version>
<scope>compile</scope>
</dependency>
</dependencies>
@@ -87,7 +87,7 @@ dependencyResolutionManagement {
}
dependencies {
implementation 'com.github.jwdeveloper.TikTok-Live-Java:Client:1.0.16-Release'
implementation 'com.github.jwdeveloper.TikTok-Live-Java:Client:1.1.0-Release'
}
```
@@ -166,37 +166,79 @@ TikTokLive.newClient("bangbetmenygy")
## Events
**Control**:
- [onConnected](#onconnected-tiktokconnectedevent)
- [onDisconnected](#ondisconnected-tiktokdisconnectedevent)
- [onReconnecting](#onreconnecting-tiktokreconnectingevent)
- [onError](#onerror-tiktokerrorevent)
- [onConnected](#onconnected-tiktokconnectedevent)
- [onDisconnected](#ondisconnected-tiktokdisconnectedevent)
**Message**:
- [onEvent](#onevent-tiktokevent)
- [onSubscribe](#onsubscribe-tiktoksubscribeevent)
- [onQuestion](#onquestion-tiktokquestionevent)
- [onFollow](#onfollow-tiktokfollowevent)
- [onLike](#onlike-tiktoklikeevent)
- [onLiveEnded](#onliveended-tiktokliveendedevent)
- [onRoomInfo](#onroominfo-tiktokroominfoevent)
- [onShare](#onshare-tiktokshareevent)
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
- [onEmote](#onemote-tiktokemoteevent)
- [onGift](#ongift-tiktokgiftevent)
- [onEvent](#onevent-tiktokevent)
- [onComment](#oncomment-tiktokcommentevent)
- [onLivePaused](#onlivepaused-tiktoklivepausedevent)
- [onRoomInfo](#onroominfo-tiktokroominfoevent)
- [onGift](#ongift-tiktokgiftevent)
- [onSubscribe](#onsubscribe-tiktoksubscribeevent)
- [onFollow](#onfollow-tiktokfollowevent)
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
- [onLiveEnded](#onliveended-tiktokliveendedevent)
- [onQuestion](#onquestion-tiktokquestionevent)
- [onShare](#onshare-tiktokshareevent)
- [onLiveUnpaused](#onliveunpaused-tiktokliveunpausedevent)
- [onEmote](#onemote-tiktokemoteevent)
- [onJoin](#onjoin-tiktokjoinevent)
- [onLike](#onlike-tiktoklikeevent)
- [onLivePaused](#onlivepaused-tiktoklivepausedevent)
**Debug**:
- [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent)
- [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent)
- [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent)
- [onHttpResponse](#onhttpresponse-tiktokhttpresponseevent)
- [onWebsocketMessage](#onwebsocketmessage-tiktokwebsocketmessageevent)
# Examples
<br>
## onReconnecting [TikTokReconnectingEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokReconnectingEvent.java)
```java
TikTokLive.newClient("host-name")
.onReconnecting((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onError [TikTokErrorEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokErrorEvent.java)
General error event. You should handle this.
```java
TikTokLive.newClient("host-name")
.onError((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onConnected [TikTokConnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokConnectedEvent.java)
@@ -238,32 +280,15 @@ TikTokLive.newClient("host-name")
<br>
## onReconnecting [TikTokReconnectingEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokReconnectingEvent.java)
## onEvent [TikTokEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/common/TikTokEvent.java)
```java
TikTokLive.newClient("host-name")
.onReconnecting((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onError [TikTokErrorEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokErrorEvent.java)
General error event. You should handle this.
Base class for all events
```java
TikTokLive.newClient("host-name")
.onError((liveClient, event) ->
.onEvent((liveClient, event) ->
{
})
@@ -293,91 +318,15 @@ TikTokLive.newClient("host-name")
<br>
## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java)
## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java)
Triggers when a user creates a subscription.
Triggered every time a new chat comment arrives.
```java
TikTokLive.newClient("host-name")
.onSubscribe((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onQuestion [TikTokQuestionEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokQuestionEvent.java)
Triggered every time someone asks a new question via the question feature.
```java
TikTokLive.newClient("host-name")
.onQuestion((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onFollow [TikTokFollowEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokFollowEvent.java)
Triggers when a user follows the streamer. Based on social event.
```java
TikTokLive.newClient("host-name")
.onFollow((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLike [TikTokLikeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokLikeEvent.java)
Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok.
```java
TikTokLive.newClient("host-name")
.onLike((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLiveEnded [TikTokLiveEndedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveEndedEvent.java)
Triggered when the live stream gets terminated by the host. Will also trigger the TikTokDisconnectedEvent event.
```java
TikTokLive.newClient("host-name")
.onLiveEnded((liveClient, event) ->
.onComment((liveClient, event) ->
{
})
@@ -391,6 +340,8 @@ TikTokLive.newClient("host-name")
## onRoomInfo [TikTokRoomInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java)
Triggered when LiveRoomInfo got updated such as likes, viewers, ranking ....
```java
TikTokLive.newClient("host-name")
@@ -403,73 +354,6 @@ TikTokLive.newClient("host-name")
<br>
## onShare [TikTokShareEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokShareEvent.java)
Triggers when a user shares the stream. Based on social event.
```java
TikTokLive.newClient("host-name")
.onShare((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java)
Triggered every time gift is sent
@see GiftSendType it has 3 states
<p>Example when user sends gift with combo</p>
<p>>Combo: 1 -> comboState = GiftSendType.Begin</p>
<p>Combo: 4 -> comboState = GiftSendType.Active</p>
<p>Combo: 8 -> comboState = GiftSendType.Active</p>
<p>Combo: 12 -> comboState = GiftSendType.Finished</p>
Remember if comboState is Finished both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered
```java
TikTokLive.newClient("host-name")
.onGiftCombo((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onEmote [TikTokEmoteEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokEmoteEvent.java)
Triggered every time a subscriber sends an emote (sticker).
```java
TikTokLive.newClient("host-name")
.onEmote((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java)
@@ -493,15 +377,15 @@ TikTokLive.newClient("host-name")
<br>
## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java)
## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java)
Triggered every time a new chat comment arrives.
Triggers when a user creates a subscription.
```java
TikTokLive.newClient("host-name")
.onComment((liveClient, event) ->
.onSubscribe((liveClient, event) ->
{
})
@@ -512,13 +396,101 @@ TikTokLive.newClient("host-name")
<br>
## onLivePaused [TikTokLivePausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLivePausedEvent.java)
## onFollow [TikTokFollowEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokFollowEvent.java)
Triggers when a user follows the streamer. Based on social event.
```java
TikTokLive.newClient("host-name")
.onLivePaused((liveClient, event) ->
.onFollow((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java)
Triggered every time gift is sent
@see GiftSendType it has 3 states
<p>Example when user sends gift with combo</p>
<p>>Combo: 1 -> comboState = GiftSendType.Begin</p>
<p>Combo: 4 -> comboState = GiftSendType.Active</p>
<p>Combo: 8 -> comboState = GiftSendType.Active</p>
<p>Combo: 12 -> comboState = GiftSendType.Finsihed</p>
<p>
Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered
```java
TikTokLive.newClient("host-name")
.onGiftCombo((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLiveEnded [TikTokLiveEndedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveEndedEvent.java)
Triggered when the live stream gets terminated by the host. Will also trigger the TikTokDisconnectedEvent event.
```java
TikTokLive.newClient("host-name")
.onLiveEnded((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onQuestion [TikTokQuestionEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokQuestionEvent.java)
Triggered every time someone asks a new question via the question feature.
```java
TikTokLive.newClient("host-name")
.onQuestion((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onShare [TikTokShareEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokShareEvent.java)
Triggers when a user shares the stream. Based on social event.
```java
TikTokLive.newClient("host-name")
.onShare((liveClient, event) ->
{
})
@@ -544,6 +516,25 @@ TikTokLive.newClient("host-name")
<br>
## onEmote [TikTokEmoteEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokEmoteEvent.java)
Triggered every time a subscriber sends an emote (sticker).
```java
TikTokLive.newClient("host-name")
.onEmote((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java)
@@ -561,6 +552,59 @@ TikTokLive.newClient("host-name")
<br>
## onLike [TikTokLikeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokLikeEvent.java)
Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok.
```java
TikTokLive.newClient("host-name")
.onLike((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onLivePaused [TikTokLivePausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLivePausedEvent.java)
```java
TikTokLive.newClient("host-name")
.onLivePaused((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onWebsocketResponse [TikTokWebsocketResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketResponseEvent.java)
```java
TikTokLive.newClient("host-name")
.onWebsocketResponse((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onWebsocketUnhandledMessage [TikTokWebsocketUnhandledMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketUnhandledMessageEvent.java)
@@ -582,17 +626,17 @@ TikTokLive.newClient("host-name")
<br>
## onWebsocketResponse [TikTokWebsocketResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketResponseEvent.java)
## onHttpResponse [TikTokHttpResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/http/TikTokHttpResponseEvent.java)
```java
TikTokLive.newClient("host-name")
.onWebsocketResponse((liveClient, event) ->
{
.onHttpResponse((liveClient, event) ->
{
})
.buildAndConnect();
})
.buildAndConnect();
```
@@ -602,24 +646,37 @@ TikTokLive.newClient("host-name")
## onWebsocketMessage [TikTokWebsocketMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketMessageEvent.java)
Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case.
Triggered every time TikTok sends data. Data incoming as protobuf message.
You can deserialize the binary object depending on the use case.
```java
TikTokLive.newClient("host-name")
.onWebsocketMessage((liveClient, event) ->
{
.onWebsocketMessage((liveClient, event) ->
{
})
.buildAndConnect();
})
.buildAndConnect();
```
<br>
## Extensions
List of extensions (addons) to TiktokLiveJava
that will save your time
- [Video Recorder](https://github.com/jwdeveloper/TikTokLiveJava/tree/master/extension-recorder)
- [Live data collector to database](https://github.com/jwdeveloper/TikTokLiveJava/tree/master/extension-collector)
## Listeners
```java
@@ -694,4 +751,7 @@ public static class CustomListener implements TikTokEventListener {
## Contributing
[Library documentation for contributors](https://github.com/jwdeveloper/TikTokLiveJava/wiki)
Your improvements are welcome! Feel free to open an <a href="https://github.com/jwdeveloper/TikTok-Live-Java/issues">issue</a> or <a href="https://github.com/jwdeveloper/TikTok-Live-Java/pulls">pull request</a>.

View File

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

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<version>1.0.17-Release</version>
<version>1.2.0-Release</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Tools-EventsWebViewer</artifactId>

View File

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

View File

@@ -97,10 +97,14 @@ public class CodeExample {
//RoomId can be used as an override if you're having issues with HostId.
//You can find it in the HTML for the livestream-page
settings.setRoomId("XXXXXXXXXXXXXXXXX");
//Optional:
//API Key for increased limit to signing server
settings.setApiKey("XXXXXXXXXXXXXXXXX");
})
.buildAndConnect();
// </code>
}
}
}

View File

@@ -126,7 +126,6 @@ TikTokLive.newClient("bangbetmenygy")
{
settings.setHostName("bangbetmenygy"); // This method is useful in case you want change hostname later
settings.setClientLanguage("en"); // Language
settings.setTimeout(Duration.ofSeconds(2)); // Connection timeout
settings.setLogLevel(Level.ALL); // Log level
settings.setPrintToConsole(true); // Printing all logs to console even if log level is Level.OFF
settings.setRetryOnConnectionFailure(true); // Reconnecting if TikTok user is offline
@@ -152,35 +151,73 @@ TikTokLive.newClient("bangbetmenygy")
**Control**:
- [onConnected](#onconnected-tiktokconnectedevent)
- [onDisconnected](#ondisconnected-tiktokdisconnectedevent)
- [onReconnecting](#onreconnecting-tiktokreconnectingevent)
- [onError](#onerror-tiktokerrorevent)
- [onConnected](#onconnected-tiktokconnectedevent)
- [onDisconnected](#ondisconnected-tiktokdisconnectedevent)
**Message**:
- [onEvent](#onevent-tiktokevent)
- [onSubscribe](#onsubscribe-tiktoksubscribeevent)
- [onQuestion](#onquestion-tiktokquestionevent)
- [onFollow](#onfollow-tiktokfollowevent)
- [onLike](#onlike-tiktoklikeevent)
- [onLiveEnded](#onliveended-tiktokliveendedevent)
- [onRoomInfo](#onroominfo-tiktokroominfoevent)
- [onShare](#onshare-tiktokshareevent)
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
- [onEmote](#onemote-tiktokemoteevent)
- [onGift](#ongift-tiktokgiftevent)
- [onEvent](#onevent-tiktokevent)
- [onComment](#oncomment-tiktokcommentevent)
- [onLivePaused](#onlivepaused-tiktoklivepausedevent)
- [onRoomInfo](#onroominfo-tiktokroominfoevent)
- [onGift](#ongift-tiktokgiftevent)
- [onSubscribe](#onsubscribe-tiktoksubscribeevent)
- [onFollow](#onfollow-tiktokfollowevent)
- [onGiftCombo](#ongiftcombo-tiktokgiftcomboevent)
- [onLiveEnded](#onliveended-tiktokliveendedevent)
- [onQuestion](#onquestion-tiktokquestionevent)
- [onShare](#onshare-tiktokshareevent)
- [onLiveUnpaused](#onliveunpaused-tiktokliveunpausedevent)
- [onEmote](#onemote-tiktokemoteevent)
- [onJoin](#onjoin-tiktokjoinevent)
- [onLike](#onlike-tiktoklikeevent)
- [onLivePaused](#onlivepaused-tiktoklivepausedevent)
**Debug**:
- [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent)
- [onWebsocketResponse](#onwebsocketresponse-tiktokwebsocketresponseevent)
- [onWebsocketUnhandledMessage](#onwebsocketunhandledmessage-tiktokwebsocketunhandledmessageevent)
- [onHttpResponse](#onhttpresponse-tiktokhttpresponseevent)
- [onWebsocketMessage](#onwebsocketmessage-tiktokwebsocketmessageevent)
# Examples
<br>
## onReconnecting [TikTokReconnectingEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokReconnectingEvent.java)
```java
TikTokLive.newClient("host-name")
.onReconnecting((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onError [TikTokErrorEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokErrorEvent.java)
General error event. You should handle this.
```java
TikTokLive.newClient("host-name")
.onError((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onConnected [TikTokConnectedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokConnectedEvent.java)
@@ -222,32 +259,15 @@ TikTokLive.newClient("host-name")
<br>
## onReconnecting [TikTokReconnectingEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokReconnectingEvent.java)
## onEvent [TikTokEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/common/TikTokEvent.java)
```java
TikTokLive.newClient("host-name")
.onReconnecting((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onError [TikTokErrorEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokErrorEvent.java)
General error event. You should handle this.
Base class for all events
```java
TikTokLive.newClient("host-name")
.onError((liveClient, event) ->
.onEvent((liveClient, event) ->
{
})
@@ -277,15 +297,15 @@ TikTokLive.newClient("host-name")
<br>
## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java)
## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java)
Triggers when a user creates a subscription.
Triggered every time a new chat comment arrives.
```java
TikTokLive.newClient("host-name")
.onSubscribe((liveClient, event) ->
.onComment((liveClient, event) ->
{
})
@@ -296,15 +316,55 @@ TikTokLive.newClient("host-name")
<br>
## onQuestion [TikTokQuestionEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokQuestionEvent.java)
## onRoomInfo [TikTokRoomInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java)
Triggered every time someone asks a new question via the question feature.
Triggered when LiveRoomInfo got updated such as likes, viewers, ranking ....
```java
TikTokLive.newClient("host-name")
.onQuestion((liveClient, event) ->
.onRoomInfo((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java)
Triggered when user sends gifts that has
no combo (most of expensive gifts)
or if combo has finished
```java
TikTokLive.newClient("host-name")
.onGift((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onSubscribe [TikTokSubscribeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokSubscribeEvent.java)
Triggers when a user creates a subscription.
```java
TikTokLive.newClient("host-name")
.onSubscribe((liveClient, event) ->
{
})
@@ -334,15 +394,25 @@ TikTokLive.newClient("host-name")
<br>
## onLike [TikTokLikeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokLikeEvent.java)
## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java)
Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok.
Triggered every time gift is sent
@see GiftSendType it has 3 states
<p>Example when user sends gift with combo</p>
<p>>Combo: 1 -> comboState = GiftSendType.Begin</p>
<p>Combo: 4 -> comboState = GiftSendType.Active</p>
<p>Combo: 8 -> comboState = GiftSendType.Active</p>
<p>Combo: 12 -> comboState = GiftSendType.Finsihed</p>
<p>
Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered
```java
TikTokLive.newClient("host-name")
.onLike((liveClient, event) ->
.onGiftCombo((liveClient, event) ->
{
})
@@ -372,13 +442,15 @@ TikTokLive.newClient("host-name")
<br>
## onRoomInfo [TikTokRoomInfoEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/room/TikTokRoomInfoEvent.java)
## onQuestion [TikTokQuestionEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokQuestionEvent.java)
Triggered every time someone asks a new question via the question feature.
```java
TikTokLive.newClient("host-name")
.onRoomInfo((liveClient, event) ->
.onQuestion((liveClient, event) ->
{
})
@@ -408,25 +480,13 @@ TikTokLive.newClient("host-name")
<br>
## onGiftCombo [TikTokGiftComboEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftComboEvent.java)
## onLiveUnpaused [TikTokLiveUnpausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java)
Triggered every time gift is sent
@see GiftSendType it has 3 states
<p>Example when user sends gift with combo</p>
<p>>Combo: 1 -> comboState = GiftSendType.Begin</p>
<p>Combo: 4 -> comboState = GiftSendType.Active</p>
<p>Combo: 8 -> comboState = GiftSendType.Active</p>
<p>Combo: 12 -> comboState = GiftSendType.Finsihed</p>
Remember if comboState is Finsihed both TikTokGiftComboEvent and TikTokGiftEvent event gets triggered
```java
TikTokLive.newClient("host-name")
.onGiftCombo((liveClient, event) ->
.onLiveUnpaused((liveClient, event) ->
{
})
@@ -456,17 +516,13 @@ TikTokLive.newClient("host-name")
<br>
## onGift [TikTokGiftEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/gift/TikTokGiftEvent.java)
## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java)
Triggered when user sends gifts that has
no combo (most of expensive gifts)
or if combo has finished
```java
TikTokLive.newClient("host-name")
.onGift((liveClient, event) ->
.onJoin((liveClient, event) ->
{
})
@@ -477,15 +533,15 @@ TikTokLive.newClient("host-name")
<br>
## onComment [TikTokCommentEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokCommentEvent.java)
## onLike [TikTokLikeEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokLikeEvent.java)
Triggered every time a new chat comment arrives.
Triggered when a viewer sends likes to the streamer. For streams with many viewers, this event is not always triggered by TikTok.
```java
TikTokLive.newClient("host-name")
.onComment((liveClient, event) ->
.onLike((liveClient, event) ->
{
})
@@ -513,30 +569,13 @@ TikTokLive.newClient("host-name")
<br>
## onLiveUnpaused [TikTokLiveUnpausedEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLiveUnpausedEvent.java)
## onWebsocketResponse [TikTokWebsocketResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketResponseEvent.java)
```java
TikTokLive.newClient("host-name")
.onLiveUnpaused((liveClient, event) ->
{
})
.buildAndConnect();
```
<br>
## onJoin [TikTokJoinEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/social/TikTokJoinEvent.java)
```java
TikTokLive.newClient("host-name")
.onJoin((liveClient, event) ->
.onWebsocketResponse((liveClient, event) ->
{
})
@@ -566,13 +605,13 @@ TikTokLive.newClient("host-name")
<br>
## onWebsocketResponse [TikTokWebsocketResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketResponseEvent.java)
## onHttpResponse [TikTokHttpResponseEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/http/TikTokHttpResponseEvent.java)
```java
TikTokLive.newClient("host-name")
.onWebsocketResponse((liveClient, event) ->
.onHttpResponse((liveClient, event) ->
{
})
@@ -586,7 +625,8 @@ TikTokLive.newClient("host-name")
## onWebsocketMessage [TikTokWebsocketMessageEvent](https://github.com/jwdeveloper/TikTokLiveJava/blob/master/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/websocket/TikTokWebsocketMessageEvent.java)
Triggered every time a protobuf encoded webcast message arrives. You can deserialize the binary object depending on the use case.
Triggered every time TikTok sends data. Data incoming as protobuf message.
You can deserialize the binary object depending on the use case.
```java
@@ -602,6 +642,17 @@ TikTokLive.newClient("host-name")
{{for item of data }}
{{if item is 2}}
my name is {{item.name}}
{{else}}
{{end}}
{{end}}
<br>
## Listeners
@@ -628,7 +679,7 @@ TikTokLive.newClient("host-name")
/**
*
* 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
@@ -636,24 +687,24 @@ TikTokLive.newClient("host-name")
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 :)";
@@ -666,7 +717,7 @@ TikTokLive.newClient("host-name")
liveClient.getLogger().info(message);
}
@TikTokEventHandler
@TikTokEventObserver
public void onAnyEvent(LiveClient liveClient, TikTokEvent event) {
liveClient.getLogger().info(event.getClass().getSimpleName());
}

View File

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

View File

@@ -0,0 +1,64 @@
Collects live data to mongodb database
```xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
<artifactId>Client</artifactId>
<version>1.1.0-Release</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
<artifactId>extension-collector</artifactId>
<version>1.1.0-Release</version>
</dependency>
</dependencies>
```
Usage
```java
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 = "Tag1";
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();
}
```

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>TikTokLiveJava</artifactId>
<version>1.0.17-Release</version>
<version>1.2.0-Release</version>
</parent>
@@ -33,7 +33,7 @@
<dependency>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>API</artifactId>
<version>1.0.17-Release</version>
<version>1.2.0-Release</version>
<scope>compile</scope>
</dependency>
</dependencies>

View File

@@ -0,0 +1,56 @@
Records stream to flv file
```xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
<artifactId>Client</artifactId>
<version>1.1.0-Release</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
<artifactId>extension-recorder</artifactId>
<version>1.1.0-Release</version>
</dependency>
</dependencies>
```
Usage
```java
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();
}
```

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId>
<version>1.0.17-Release</version>
<version>1.2.0-Release</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>extension-recorder</artifactId>

View File

@@ -30,7 +30,6 @@ import java.util.function.Consumer;
public class TikTokLiveRecorder
{
public static LiveRecorder use(Consumer<RecorderSettings> consumer)
{
return new RecorderListener(consumer);
@@ -40,4 +39,4 @@ public class TikTokLiveRecorder
{
return use(x ->{});
}
}
}

View File

@@ -24,24 +24,17 @@ 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.events.*;
import io.github.jwdeveloper.tiktok.data.events.http.TikTokRoomDataResponseEvent;
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.data.*;
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 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;
@@ -76,66 +69,53 @@ public class RecorderListener implements LiveRecorder {
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!");
downloadData = settings.getPrepareDownloadData() != null ? settings.getPrepareDownloadData().apply(json) : mapToDownloadData(json);
if (downloadData.getDownloadLiveUrl().isEmpty())
liveClient.getLogger().warning("Unable to find download live url!");
else
liveClient.getLogger().info("Live download url found!");
}
@TikTokEventObserver
private void onConnected(LiveClient liveClient, TikTokConnectedEvent event) {
liveDownloadThread = new Thread(() ->
{
liveDownloadThread = new Thread(() -> {
try {
var bufferSize = 1024;
var url = new URL(downloadData.getFullUrl());
HttpsURLConnection socksConnection = (HttpsURLConnection) url.openConnection();
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
var headers = LiveClientSettings.DefaultRequestHeaders();
for (var entry : headers.entrySet()) {
socksConnection.setRequestProperty(entry.getKey(), entry.getValue());
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
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;
var in = new BufferedInputStream(connection.getInputStream());
var path = settings.getOutputPath() + File.separator + settings.getOutputFileName();
var file = new File(path);
file.getParentFile().mkdirs();
var fileOutputStream = new FileOutputStream(file);
byte[] dataBuffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = in.read(dataBuffer, 0, bufferSize)) != -1) {
fileOutputStream.write(dataBuffer, 0, bytesRead);
}
} catch (Exception e) {
in.close();
} 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) {
int count;
while ((count = bis.read(buffer, 0, 1024)) != -1)
fis.write(buffer, 0, count);
}
fis.close();
bis.close();
}
@@ -143,25 +123,23 @@ public class RecorderListener implements LiveRecorder {
@TikTokEventObserver
private void onDisconnected(LiveClient liveClient, TikTokDisconnectedEvent event) {
liveDownloadThread.interrupt();
if (isConnected())
liveDownloadThread.interrupt();
}
@TikTokEventObserver
private void onDisconnected(LiveClient liveClient, TikTokLiveEndedEvent event) {
liveDownloadThread.interrupt();
if (isConnected())
liveDownloadThread.interrupt();
}
private int terminateFfmpeg(final Process process) {
if (!process.isAlive()) {
/*
* ffmpeg -version, do nothing
*/
// ffmpeg -version, do nothing
return process.exitValue();
}
/*
* ffmpeg -f x11grab
*/
// 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');
@@ -174,7 +152,7 @@ public class RecorderListener implements LiveRecorder {
process.waitFor();
}
return process.exitValue();
} catch (final InterruptedException ie) {
} catch (InterruptedException ie) {
System.out.println("Interrupted");
ie.printStackTrace();
Thread.currentThread().interrupt();
@@ -201,12 +179,10 @@ public class RecorderListener implements LiveRecorder {
.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
@@ -216,5 +192,7 @@ public class RecorderListener implements LiveRecorder {
return new DownloadData(urlLink, sessionId);
}
}
private boolean isConnected() {
return liveDownloadThread != null && liveDownloadThread.isAlive();
}
}

View File

@@ -34,6 +34,6 @@ public class DownloadData {
private String sessionId;
public String getFullUrl() {
return downloadLiveUrl + "?_webnoredir=1&session_id=" + sessionId;
return downloadLiveUrl + "&_webnoredir=1&session_id=" + sessionId;
}
}
}

View File

@@ -29,25 +29,6 @@ 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 {
@@ -63,20 +44,11 @@ public class RecorderSettings {
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();
}
}
}

View File

@@ -7,7 +7,7 @@
<groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>TikTokLiveJava</artifactId>
<packaging>pom</packaging>
<version>1.0.17-Release</version>
<version>1.2.0-Release</version>
<modules>
<module>API</module>
<module>Client</module>