Compare commits

..

35 Commits

Author SHA1 Message Date
Jacek W
bf755bcb89 Merge pull request #98 from lulajax/fix-issue-97
adjust the position of the followingCount and followers
2024-08-23 09:23:49 +02:00
lujunjie
20bb05e527 adjust the position of the followingCount and followers 2024-08-23 15:13:53 +08:00
Jacek W
d5a3213ad8 Update README.md 2024-08-20 20:47:17 +02:00
Jacek W
7e496373ef Update README.md 2024-08-20 20:47:00 +02:00
GitHub Action
1f3c1c7d41 Update version in pom.xml 2024-07-31 02:47:35 +00:00
David Kohler
c160259863 Merge pull request #94 from jwdeveloper/develop-1.8.5
Develop 1.8.5
2024-07-30 22:45:49 -04:00
kohlerpop1
fb458e7e7d Rename pinging-task to heartbeat-task! 2024-07-30 20:36:45 -04:00
kohlerpop1
5f5ada312a Renamed PingTask to HeartbeatTask to reflect discovered byte array of TikTok's custom heartbeat value 2024-07-30 16:09:50 -04:00
GitHub Action
cb20c3dd3a Update version in pom.xml 2024-07-27 23:20:36 +00:00
David Kohler
bf42f65b3d Merge pull request #93 from jwdeveloper/develop-1.8.4
Added User instance to LiveUserDataMapper Response to access the retrieved user
2024-07-27 19:18:58 -04:00
kohlerpop1
05e18ef8e0 Added User instance to LiveUserDataMapper Response to access the retrieved user. 2024-07-27 13:02:31 -04:00
GitHub Action
0f6ee58d7f Update version in pom.xml 2024-07-21 17:03:47 +00:00
Jacek W
511759960d Merge pull request #91 from jwdeveloper/develop-1.8.3
- fix tests
2024-07-21 19:02:02 +02:00
JW
617d1c381a - fix tests 2024-07-21 19:01:11 +02:00
Jacek W
6f8b4698cd Merge pull request #90 from jwdeveloper/develop-1.8.3
Develop 1.8.3
2024-07-21 18:56:48 +02:00
JW
2bc1993ea5 - Create of methods for more events
- User.name was empty in testing event
- Add `onConnecting` to builder
2024-07-21 12:08:27 +02:00
kohlerpop1
3d4a517adb Removal of TikTokRoomDataResponseEvent.java as its included in TikTokPreConnectionEvent.java
Added ListUser#toString
Changed TikTokLiveHttpClient.getStartingPayload and TikTokLiveHttpClient.getByteResponse to protected
Altered DownloadData to be dynamic to declare request values or append them instead!
2024-07-18 17:41:27 -04:00
GitHub Action
ee0b559758 Update version in pom.xml 2024-07-06 15:26:51 +00:00
GitHub Action
1c9573dc39 Update version in pom.xml 2024-07-06 15:13:34 +00:00
Jacek W
5b17c33236 Merge pull request #89 from jwdeveloper/develop-1.8.2
Develop 1.8.2
2024-07-06 17:12:01 +02:00
kohlerpop1
b59373254c Make TikTokLinkMicBattleEvent.is1v1 and is2v2 dynamic based upon team instance
Moved Team1v1 and Team2v2 totalPoints to Team for universal access
2024-07-05 16:21:42 -04:00
jacek.wolniewicz
5524d9f8c4 -- tests improvement 2024-07-05 13:23:56 +02:00
jacek.wolniewicz
aa56f8eaea -- tests improvement 2024-07-05 13:21:59 +02:00
jacek.wolniewicz
e40bde8e7c - clean up code
- rename module Example to examples
2024-07-04 17:40:22 +02:00
jacek.wolniewicz
b769eb9c1f Merge remote-tracking branch 'origin/develop-1.8.2' into develop-1.8.2 2024-07-04 11:52:26 +02:00
jacek.wolniewicz
3e555a502a - add comments next to magic numbers 2024-07-04 11:51:45 +02:00
kohlerpop1
316868818b Renamed Priority.priorityValue to value to follow enum standards
Clarified message in TikTokEventListener
2024-07-03 21:04:44 -04:00
jacek.wolniewicz
36475c2cf6 - change method name from onMapping, to mappings 2024-07-04 00:00:26 +02:00
jacek.wolniewicz
2dbe81278c Merge branch 'refs/heads/master' into develop-1.8.2 2024-07-03 23:45:11 +02:00
jacek.wolniewicz
91fc32b20b Improvement on the ListenersManager 2024-07-03 23:43:23 +02:00
jacek.wolniewicz
fa855fa3aa Improvement on the ListenersManager 2024-07-03 23:40:48 +02:00
jacek.wolniewicz
cfea12dacc Remove descrabble 2024-07-03 22:37:40 +02:00
Jacek W
16cd819aaf Merge pull request #88 from jwdeveloper/develop-1.8.1
Develop 1.8.1
2024-07-03 22:30:18 +02:00
jacek.wolniewicz
290a4970fd Improvement of Listener 2024-07-03 22:29:11 +02:00
jacek.wolniewicz
660cb287e9 Improvement of Listener 2024-07-03 22:27:15 +02:00
72 changed files with 630 additions and 384 deletions

View File

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

View File

@@ -0,0 +1,18 @@
package io.github.jwdeveloper.tiktok.annotations;
/**
* HIGHEST 1
* HIGH 2
* NORMAL 3
* LOW 4
* LOWEST 5
*/
public enum Priority {
LOWEST(2), LOW(1), NORMAL(0), HIGH(-1), HIGHEST(-2);
public final int value;
Priority(int value) {
this.value = value;
}
}

View File

@@ -28,5 +28,14 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface TikTokEventObserver public @interface TikTokEventObserver
{ {
/**
* When more than one method listen for certain Event, you can specify the method priority
* @see Priority
*/
Priority priority() default Priority.NORMAL;
/**
* When true, action is invoked on a thread, from the threads pool
*/
boolean async() default false;
} }

View File

@@ -58,6 +58,7 @@ public class TikTokCommentEvent extends TikTokHeaderEvent {
var builder = WebcastChatMessage.newBuilder(); var builder = WebcastChatMessage.newBuilder();
builder.setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder() builder.setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder()
.setNickname(userName) .setNickname(userName)
.setDisplayId(userName)
.build()); .build());
builder.setContentLanguage("en"); builder.setContentLanguage("en");
builder.setVisibleToSender(true); builder.setVisibleToSender(true);

View File

@@ -29,4 +29,9 @@ import io.github.jwdeveloper.tiktok.data.events.common.TikTokLiveClientEvent;
public class TikTokConnectedEvent extends TikTokLiveClientEvent public class TikTokConnectedEvent extends TikTokLiveClientEvent
{ {
public static TikTokConnectedEvent of()
{
return new TikTokConnectedEvent();
}
} }

View File

@@ -39,4 +39,10 @@ public class TikTokDisconnectedEvent extends TikTokLiveClientEvent {
public TikTokDisconnectedEvent() { public TikTokDisconnectedEvent() {
this("None"); this("None");
} }
public static TikTokDisconnectedEvent of(String reason)
{
return new TikTokDisconnectedEvent(reason);
}
} }

View File

@@ -25,13 +25,18 @@ package io.github.jwdeveloper.tiktok.data.events;
import io.github.jwdeveloper.tiktok.annotations.EventMeta; import io.github.jwdeveloper.tiktok.annotations.EventMeta;
import io.github.jwdeveloper.tiktok.annotations.EventType; import io.github.jwdeveloper.tiktok.annotations.EventType;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokLiveClientEvent; import io.github.jwdeveloper.tiktok.data.events.common.TikTokLiveClientEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
@EventMeta(eventType = EventType.Control) @EventMeta(eventType = EventType.Control)
public class TikTokErrorEvent extends TikTokLiveClientEvent public class TikTokErrorEvent extends TikTokLiveClientEvent {
{
private final Throwable exception; private final Throwable exception;
public static TikTokErrorEvent of(String message) {
return new TikTokErrorEvent(new TikTokLiveException(message));
}
} }

View File

@@ -43,8 +43,6 @@ public class TikTokLinkMicBattleEvent extends TikTokHeaderEvent
true if battle is finished otherwise false true if battle is finished otherwise false
*/ */
private final boolean finished; private final boolean finished;
@Getter(AccessLevel.NONE)
private final boolean oneVsOne;
private final List<Team> teams; private final List<Team> teams;
public TikTokLinkMicBattleEvent(WebcastLinkMicBattle msg) { public TikTokLinkMicBattleEvent(WebcastLinkMicBattle msg) {
@@ -55,7 +53,6 @@ public class TikTokLinkMicBattleEvent extends TikTokHeaderEvent
if (msg.getHostTeamCount() == 2) { // 1v1 battle if (msg.getHostTeamCount() == 2) { // 1v1 battle
teams.add(new Team1v1(msg.getHostTeam(0), msg)); teams.add(new Team1v1(msg.getHostTeam(0), msg));
teams.add(new Team1v1(msg.getHostTeam(1), msg)); teams.add(new Team1v1(msg.getHostTeam(1), msg));
oneVsOne = true;
} else { // 2v2 battle } else { // 2v2 battle
if (isFinished()) { if (isFinished()) {
teams.add(new Team2v2(msg.getHostData2V2List().stream().filter(data -> data.getTeamNumber() == 1).findFirst().orElse(null), msg)); teams.add(new Team2v2(msg.getHostData2V2List().stream().filter(data -> data.getTeamNumber() == 1).findFirst().orElse(null), msg));
@@ -64,7 +61,6 @@ public class TikTokLinkMicBattleEvent extends TikTokHeaderEvent
teams.add(new Team2v2(msg.getHostTeam(0), msg.getHostTeam(1), msg)); teams.add(new Team2v2(msg.getHostTeam(0), msg.getHostTeam(1), msg));
teams.add(new Team2v2(msg.getHostTeam(2), msg.getHostTeam(3), msg)); teams.add(new Team2v2(msg.getHostTeam(2), msg.getHostTeam(3), msg));
} }
oneVsOne = false;
} }
// Info: // Info:
@@ -74,10 +70,14 @@ public class TikTokLinkMicBattleEvent extends TikTokHeaderEvent
} }
public boolean is1v1() { public boolean is1v1() {
return oneVsOne; return teams.get(0) instanceof Team1v1;
} }
public boolean is2v2() { public boolean is2v2() {
return !oneVsOne; return teams.get(0) instanceof Team2v2;
}
public boolean isTie() {
return isFinished() && teams.get(0).getTotalPoints() == teams.get(1).getTotalPoints();
} }
} }

View File

@@ -24,9 +24,14 @@ package io.github.jwdeveloper.tiktok.data.events;
import io.github.jwdeveloper.tiktok.annotations.EventMeta; import io.github.jwdeveloper.tiktok.annotations.EventMeta;
import io.github.jwdeveloper.tiktok.annotations.EventType; import io.github.jwdeveloper.tiktok.annotations.EventType;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.control.TikTokConnectingEvent;
@EventMeta(eventType = EventType.Message) @EventMeta(eventType = EventType.Message)
public class TikTokLiveEndedEvent extends TikTokEvent { public class TikTokLiveEndedEvent extends TikTokEvent {
public static TikTokLiveEndedEvent of() {
return new TikTokLiveEndedEvent();
}
} }

View File

@@ -53,6 +53,7 @@ public class TikTokSubscribeEvent extends TikTokHeaderEvent {
public static TikTokSubscribeEvent of(String userName) { public static TikTokSubscribeEvent of(String userName) {
return new TikTokSubscribeEvent(WebcastMemberMessage.newBuilder() return new TikTokSubscribeEvent(WebcastMemberMessage.newBuilder()
.setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder() .setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder()
.setDisplayId(userName)
.setNickname(userName) .setNickname(userName)
.build()) .build())
.build()); .build());

View File

@@ -31,7 +31,9 @@ import io.github.jwdeveloper.tiktok.data.events.common.TikTokLiveClientEvent;
* Triggered when client is connecting to live is successfully established. * Triggered when client is connecting to live is successfully established.
*/ */
@EventMeta(eventType = EventType.Control) @EventMeta(eventType = EventType.Control)
public class TikTokConnectingEvent extends TikTokLiveClientEvent public class TikTokConnectingEvent extends TikTokLiveClientEvent {
{
public static TikTokConnectingEvent of() {
return new TikTokConnectingEvent();
}
} }

View File

@@ -1,38 +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.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;
}

View File

@@ -32,8 +32,7 @@ import lombok.Value;
@Value @Value
@EventMeta(eventType = EventType.Message) @EventMeta(eventType = EventType.Message)
public class TikTokFollowEvent extends TikTokHeaderEvent public class TikTokFollowEvent extends TikTokHeaderEvent {
{
User user; User user;
int totalFollowers; int totalFollowers;
@@ -43,12 +42,12 @@ public class TikTokFollowEvent extends TikTokHeaderEvent
totalFollowers = msg.getFollowCount(); totalFollowers = msg.getFollowCount();
} }
public static TikTokFollowEvent of(String userName) public static TikTokFollowEvent of(String userName) {
{
return new TikTokFollowEvent(WebcastSocialMessage.newBuilder() return new TikTokFollowEvent(WebcastSocialMessage.newBuilder()
.setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder() .setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder()
.setNickname(userName) .setDisplayId(userName)
.build()) .setNickname(userName)
.build()); .build())
.build());
} }
} }

View File

@@ -53,6 +53,7 @@ public class TikTokJoinEvent extends TikTokHeaderEvent {
{ {
return new TikTokJoinEvent(WebcastMemberMessage.newBuilder() return new TikTokJoinEvent(WebcastMemberMessage.newBuilder()
.setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder() .setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder()
.setDisplayId(userName)
.setNickname(userName) .setNickname(userName)
.build()) .build())
.build()); .build());

View File

@@ -62,6 +62,7 @@ public class TikTokLikeEvent extends TikTokHeaderEvent
.setCount(likes) .setCount(likes)
.setTotal(likes) .setTotal(likes)
.setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder() .setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder()
.setDisplayId(userName)
.setNickname(userName) .setNickname(userName)
.build()) .build())
.build()); .build());

View File

@@ -26,6 +26,7 @@ import io.github.jwdeveloper.tiktok.annotations.EventMeta;
import io.github.jwdeveloper.tiktok.annotations.EventType; import io.github.jwdeveloper.tiktok.annotations.EventType;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent; import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent;
import io.github.jwdeveloper.tiktok.data.models.users.User; import io.github.jwdeveloper.tiktok.data.models.users.User;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLikeMessage;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastSocialMessage; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastSocialMessage;
import lombok.Getter; import lombok.Getter;
@@ -33,18 +34,27 @@ import lombok.Getter;
@Getter @Getter
@EventMeta(eventType = EventType.Message) @EventMeta(eventType = EventType.Message)
public class TikTokShareEvent extends TikTokHeaderEvent { public class TikTokShareEvent extends TikTokHeaderEvent {
private final User user; private final User user;
private final int totalShares; private final int totalShares;
public TikTokShareEvent(WebcastSocialMessage msg, Integer amount) { public TikTokShareEvent(WebcastSocialMessage msg, Integer amount) {
super(msg.getCommon()); super(msg.getCommon());
user = User.map(msg.getUser()); user = User.map(msg.getUser());
this.totalShares = amount; this.totalShares = amount;
} }
public TikTokShareEvent(WebcastSocialMessage msg) { public TikTokShareEvent(WebcastSocialMessage msg) {
super(msg.getCommon()); super(msg.getCommon());
user = User.map(msg.getUser()); user = User.map(msg.getUser());
totalShares = 1; totalShares = 1;
} }
public static TikTokShareEvent of(String userName, int shaders) {
return new TikTokShareEvent(WebcastSocialMessage.newBuilder()
.setUser(io.github.jwdeveloper.tiktok.messages.data.User.newBuilder()
.setDisplayId(userName)
.setNickname(userName)
.build())
.build(), shaders);
}
} }

View File

@@ -22,7 +22,12 @@
*/ */
package io.github.jwdeveloper.tiktok.data.models.battles; package io.github.jwdeveloper.tiktok.data.models.battles;
import lombok.Getter;
public abstract class Team { public abstract class Team {
/** Value >= 0 when finished otherwise -1 */
@Getter protected int totalPoints;
/** /**
* Provides a check for verifying if this team represents a 1v1 Team. * Provides a check for verifying if this team represents a 1v1 Team.
* @return true if this team is of type {@link Team1v1}, false otherwise. * @return true if this team is of type {@link Team1v1}, false otherwise.

View File

@@ -29,10 +29,7 @@ import lombok.Getter;
import java.util.*; import java.util.*;
@Getter @Getter
public class Team1v1 extends Team public class Team1v1 extends Team {
{
/** Value >= 0 when finished otherwise -1 */
private final int totalPoints;
private final int winStreak; private final int winStreak;
private final User host; private final User host;
private final List<Viewer> viewers; private final List<Viewer> viewers;

View File

@@ -30,9 +30,6 @@ import java.util.*;
@Getter @Getter
public class Team2v2 extends Team { public class Team2v2 extends Team {
/** Value >= 0 when finished otherwise -1 */
private final int totalPoints;
private final List<User> hosts; private final List<User> hosts;
private final List<Viewer> viewers; private final List<Viewer> viewers;

View File

@@ -53,4 +53,19 @@ public class ListUser
AUDIO, AUDIO,
VIDEO VIDEO
} }
@Override
public String toString() {
return "ListUser{" +
"user=" + user +
", linkType=" + linkType +
", linkMicId=" + linkMicId +
", linkStatus=" + linkStatus +
", modifyTime=" + modifyTime +
", linkerId=" + linkerId +
", userPosition=" + userPosition +
", silenceStatus=" + silenceStatus +
", roleType=" + roleType +
"}";
}
} }

View File

@@ -22,6 +22,7 @@
*/ */
package io.github.jwdeveloper.tiktok.data.requests; package io.github.jwdeveloper.tiktok.data.requests;
import io.github.jwdeveloper.tiktok.data.models.users.User;
import lombok.*; import lombok.*;
public class LiveUserData { public class LiveUserData {
@@ -44,6 +45,7 @@ public class LiveUserData {
private final UserStatus userStatus; private final UserStatus userStatus;
private final String roomId; private final String roomId;
private final long startTime; private final long startTime;
private final User user;
public boolean isLiveOnline() { public boolean isLiveOnline() {
return userStatus == LiveUserData.UserStatus.Live || userStatus == LiveUserData.UserStatus.LivePaused; return userStatus == LiveUserData.UserStatus.Live || userStatus == LiveUserData.UserStatus.LivePaused;

View File

@@ -82,9 +82,9 @@ public class LiveClientSettings {
/** /**
* Interval of time in milliseconds between pings to TikTok * Interval of time in milliseconds between pings to TikTok
* @apiNote Min: 250 (0.25 seconds), Default: 5000 (5 seconds) * @apiNote Min: 250 (0.25 seconds), Default: 10000 (10 seconds - TikTok Default)
*/ */
private long pingInterval = 5000; private long pingInterval = 10000;
/** Throw an exception on 18+ Age Restriction */ /** Throw an exception on 18+ Age Restriction */
private boolean throwOnAgeRestriction; private boolean throwOnAgeRestriction;

View File

@@ -25,16 +25,13 @@ package io.github.jwdeveloper.tiktok.listener;
import java.util.List; import java.util.List;
/** /**
* You can dynamically add or removing TikTokEventListener * Manage events listeners objects
*
* @see TikTokEventListener
*
*/ */
public interface ListenersManager public interface ListenersManager
{ {
List<TikTokEventListener> getListeners(); List<Object> getListeners();
void addListener(TikTokEventListener listener); void addListener(Object listener);
void removeListener(TikTokEventListener listener); void removeListener(Object listener);
} }

View File

@@ -40,7 +40,7 @@ import io.github.jwdeveloper.tiktok.live.LiveClient;
* {@code * {@code
* public static class CustomListener implements TikTokEventListener * public static class CustomListener implements TikTokEventListener
* { * {
* @TikTokEventObserver * @TikTokEventObserver
* public void onError(LiveClient liveClient, TikTokErrorEvent event) * public void onError(LiveClient liveClient, TikTokErrorEvent event)
* { * {
* System.out.println(event.getException().getMessage()); * System.out.println(event.getException().getMessage());
@@ -67,7 +67,9 @@ import io.github.jwdeveloper.tiktok.live.LiveClient;
* } * }
* </pre> * </pre>
*/ */
public interface TikTokEventListener //TODO I think this interface can be removed, since we are using,
{ //annotation @TikTokEventHandler to check methods that are events
@Deprecated(forRemoval = true, since = "1.8.1 (This interface is not longer needed, please remove it from your class)")
public interface TikTokEventListener {
} }

View File

@@ -24,6 +24,7 @@ package io.github.jwdeveloper.tiktok.live.builder;
import io.github.jwdeveloper.tiktok.data.events.*; import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; 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.TikTokPreConnectionEvent; 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.TikTokGiftComboEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent; import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
@@ -60,6 +61,18 @@ public interface EventsBuilder<T> {
return onEvent(TikTokEvent.class, action); return onEvent(TikTokEvent.class, action);
} }
/**
* As a first event after method `LiveClient::connect()` is performed
*
* @param action consumable action
* @return self instance
*/
default T onConnecting(EventConsumer<TikTokConnectingEvent> action)
{
return onEvent(TikTokConnectingEvent.class, action);
}
/** /**
* Invoked when information about room (live) got updated such as viewer count, etc.. * Invoked when information about room (live) got updated such as viewer count, etc..
* *

View File

@@ -24,7 +24,6 @@ package io.github.jwdeveloper.tiktok.live.builder;
import io.github.jwdeveloper.dependance.implementation.DependanceContainerBuilder; import io.github.jwdeveloper.dependance.implementation.DependanceContainerBuilder;
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.mappers.LiveMapper; import io.github.jwdeveloper.tiktok.mappers.LiveMapper;
@@ -33,40 +32,43 @@ import java.util.function.Consumer;
public interface LiveClientBuilder extends EventsBuilder<LiveClientBuilder> { public interface LiveClientBuilder extends EventsBuilder<LiveClientBuilder> {
/** /**
* This method is triggered after default mappings are registered * This method is triggered after default mappings are registered
* It could be used to OVERRIDE behaviour of mappings and implement custom behaviour * It could be used to OVERRIDE behaviour of mappings and implement custom behaviour
* * <p>
* Be aware if for example you override WebcastGiftEvent, onGiftEvent() will not be working * Be aware if for example you override WebcastGiftEvent, onGiftEvent() will not be working
* *
* @param onCustomMappings lambda method * @param onCustomMappings lambda method
* @return * @return
*/ */
LiveClientBuilder onMapping(Consumer<LiveMapper> onCustomMappings); LiveClientBuilder mappings(Consumer<LiveMapper> onCustomMappings);
@Deprecated(forRemoval = true, since = "1.8.2 use `mappings` method instead")
LiveClientBuilder onMappings(Consumer<LiveMapper> onCustomMappings);
/** /**
* Configuration of client settings * Configuration of client settings
* @see LiveClientSettings *
* @param onConfigure * @param onConfigure
* @return * @return
* @see LiveClientSettings
*/ */
LiveClientBuilder configure(Consumer<LiveClientSettings> onConfigure); LiveClientBuilder configure(Consumer<LiveClientSettings> onConfigure);
/** /**
* @see TikTokEventListener
* Adding events listener class, its fancy way to register events without using lamda method * Adding events listener class, its fancy way to register events without using lamda method
* but actual method in class that implements TikTokEventListener * but actual method in class that implements TikTokEventListener
*
* @return * @return
*/ */
LiveClientBuilder addListener(TikTokEventListener listener); LiveClientBuilder addListener(Object listener);
/** /**
* Allows you to use own implementation of internal TikTokLive dependencies, * Allows you to use own implementation of internal TikTokLive dependencies,
* when the default implementation does not meet your needs * when the default implementation does not meet your needs
* *
*
* @param onCustomizeDependencies access to dependency container * @param onCustomizeDependencies access to dependency container
* @return * @return
*/ */
@@ -74,18 +76,19 @@ public interface LiveClientBuilder extends EventsBuilder<LiveClientBuilder> {
/** /**
* Builds new instance of the LiveClient * Builds new instance of the LiveClient
*
* @return LiveClient object * @return LiveClient object
*/ */
LiveClient build(); LiveClient build();
/** /**
* Builds new instance of the LiveClient and connects to live * Builds new instance of the LiveClient and connects to live
*
* @return LiveClient object * @return LiveClient object
*/ */
LiveClient buildAndConnect(); LiveClient buildAndConnect();
/** /**
*
* @return LiveClient object and connects to TikTok live asynchronously * @return LiveClient object and connects to TikTok live asynchronously
*/ */
CompletableFuture<LiveClient> buildAndConnectAsync(); CompletableFuture<LiveClient> buildAndConnectAsync();

View File

@@ -24,9 +24,8 @@ package io.github.jwdeveloper.tiktok.websocket;
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData; import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
public interface SocketClient { public interface LiveSocketClient {
void start(LiveConnectionData.Response webcastResponse, LiveClient tikTokLiveClient); void start(LiveConnectionData.Response webcastResponse, LiveClient tikTokLiveClient);
void stop(); void stop();
} }

View File

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

View File

@@ -26,7 +26,6 @@ import com.google.protobuf.ByteString;
import io.github.jwdeveloper.tiktok.data.events.*; import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.control.*; 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.events.room.TikTokRoomInfoEvent;
import io.github.jwdeveloper.tiktok.data.requests.*; import io.github.jwdeveloper.tiktok.data.requests.*;
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
@@ -36,7 +35,7 @@ import io.github.jwdeveloper.tiktok.listener.*;
import io.github.jwdeveloper.tiktok.live.*; import io.github.jwdeveloper.tiktok.live.*;
import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastResponse;
import io.github.jwdeveloper.tiktok.models.ConnectionState; import io.github.jwdeveloper.tiktok.models.ConnectionState;
import io.github.jwdeveloper.tiktok.websocket.SocketClient; import io.github.jwdeveloper.tiktok.websocket.LiveSocketClient;
import lombok.Getter; import lombok.Getter;
import java.util.Base64; import java.util.Base64;
@@ -49,7 +48,7 @@ public class TikTokLiveClient implements LiveClient
{ {
private final TikTokRoomInfo roomInfo; private final TikTokRoomInfo roomInfo;
private final LiveHttpClient httpClient; private final LiveHttpClient httpClient;
private final SocketClient webSocketClient; private final LiveSocketClient webSocketClient;
private final LiveEventsHandler tikTokEventHandler; private final LiveEventsHandler tikTokEventHandler;
private final LiveClientSettings clientSettings; private final LiveClientSettings clientSettings;
private final ListenersManager listenersManager; private final ListenersManager listenersManager;
@@ -62,7 +61,7 @@ public class TikTokLiveClient implements LiveClient
GiftsManager giftsManager, GiftsManager giftsManager,
TikTokRoomInfo tikTokLiveMeta, TikTokRoomInfo tikTokLiveMeta,
LiveHttpClient tiktokHttpClient, LiveHttpClient tiktokHttpClient,
SocketClient webSocketClient, LiveSocketClient webSocketClient,
LiveEventsHandler tikTokEventHandler, LiveEventsHandler tikTokEventHandler,
LiveClientSettings clientSettings, LiveClientSettings clientSettings,
ListenersManager listenersManager, ListenersManager listenersManager,
@@ -78,17 +77,6 @@ public class TikTokLiveClient implements LiveClient
this.logger = logger; this.logger = logger;
} }
public void connectAsync(Consumer<LiveClient> onConnection) {
connectAsync().thenAccept(onConnection);
}
public CompletableFuture<LiveClient> connectAsync() {
return CompletableFuture.supplyAsync(() -> {
connect();
return this;
});
}
public void connect() { public void connect() {
try { try {
tryConnect(); tryConnect();
@@ -100,8 +88,7 @@ public class TikTokLiveClient implements LiveClient
if (e instanceof TikTokLiveOfflineHostException && clientSettings.isRetryOnConnectionFailure()) { if (e instanceof TikTokLiveOfflineHostException && clientSettings.isRetryOnConnectionFailure()) {
try { try {
Thread.sleep(clientSettings.getRetryConnectionTimeout().toMillis()); Thread.sleep(clientSettings.getRetryConnectionTimeout().toMillis());
} catch (Exception ignored) { } catch (Exception ignored) {}
}
logger.info("Reconnecting"); logger.info("Reconnecting");
tikTokEventHandler.publish(this, new TikTokReconnectingEvent()); tikTokEventHandler.publish(this, new TikTokReconnectingEvent());
this.connect(); this.connect();
@@ -144,8 +131,6 @@ public class TikTokLiveClient implements LiveClient
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline) if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline)
throw new TikTokLiveOfflineHostException("LiveStream for " + roomInfo.getHostName() + " not found, is the Host offline?"); throw new TikTokLiveOfflineHostException("LiveStream for " + roomInfo.getHostName() + " not found, is the Host offline?");
tikTokEventHandler.publish(this, new TikTokRoomDataResponseEvent(liveData));
roomInfo.setTitle(liveData.getTitle()); roomInfo.setTitle(liveData.getTitle());
roomInfo.setViewersCount(liveData.getViewers()); roomInfo.setViewersCount(liveData.getViewers());
roomInfo.setTotalViewersCount(liveData.getTotalViewers()); roomInfo.setTotalViewersCount(liveData.getTotalViewers());
@@ -199,4 +184,15 @@ public class TikTokLiveClient implements LiveClient
messageHandler.handleSingleMessage(this, message); messageHandler.handleSingleMessage(this, message);
} }
public void connectAsync(Consumer<LiveClient> onConnection) {
connectAsync().thenAccept(onConnection);
}
public CompletableFuture<LiveClient> connectAsync() {
return CompletableFuture.supplyAsync(() -> {
connect();
return this;
});
}
} }

View File

@@ -52,7 +52,7 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
protected final LiveClientSettings clientSettings; protected final LiveClientSettings clientSettings;
protected final LiveEventsHandler eventHandler; protected final LiveEventsHandler eventHandler;
protected final List<TikTokEventListener> listeners; protected final List<Object> listeners;
protected final List<Consumer<LiveMapper>> onCustomMappings; protected final List<Consumer<LiveMapper>> onCustomMappings;
protected final List<Consumer<DependanceContainerBuilder>> onCustomDependencies; protected final List<Consumer<DependanceContainerBuilder>> onCustomDependencies;
@@ -65,17 +65,23 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
this.onCustomDependencies = new ArrayList<>(); this.onCustomDependencies = new ArrayList<>();
} }
public LiveClientBuilder onMapping(Consumer<LiveMapper> consumer) { public LiveClientBuilder mappings(Consumer<LiveMapper> consumer) {
this.onCustomMappings.add(consumer); this.onCustomMappings.add(consumer);
return this; return this;
} }
@Override
public LiveClientBuilder onMappings(Consumer<LiveMapper> onCustomMappings) {
mappings(onCustomMappings);
return this;
}
public TikTokLiveClientBuilder configure(Consumer<LiveClientSettings> onConfigure) { public TikTokLiveClientBuilder configure(Consumer<LiveClientSettings> onConfigure) {
onConfigure.accept(clientSettings); onConfigure.accept(clientSettings);
return this; return this;
} }
public TikTokLiveClientBuilder addListener(TikTokEventListener listener) { public TikTokLiveClientBuilder addListener(Object listener) {
if (listener != null) if (listener != null)
listeners.add(listener); listeners.add(listener);
return this; return this;
@@ -97,8 +103,9 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
if (clientSettings.getHostName().startsWith("@")) if (clientSettings.getHostName().startsWith("@"))
clientSettings.setHostName(clientSettings.getHostName().substring(1)); clientSettings.setHostName(clientSettings.getHostName().substring(1));
//TODO 250 Magic number
if (clientSettings.getPingInterval() < 250) if (clientSettings.getPingInterval() < 250)
throw new TikTokLiveException("Minimum allowed ping interval is 250 millseconds"); throw new TikTokLiveException("Minimum allowed ping interval is 250 milliseconds");
var httpSettings = clientSettings.getHttpSettings(); var httpSettings = clientSettings.getHttpSettings();
httpSettings.getParams().put("app_language", clientSettings.getClientLanguage()); httpSettings.getParams().put("app_language", clientSettings.getClientLanguage());
@@ -124,20 +131,19 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
//messages //messages
dependance.registerSingleton(LiveEventsHandler.class, eventHandler); dependance.registerSingleton(LiveEventsHandler.class, eventHandler);
dependance.registerSingleton(LiveMessagesHandler.class,TikTokLiveMessageHandler.class); dependance.registerSingleton(LiveMessagesHandler.class, TikTokLiveMessageHandler.class);
//listeners //listeners
dependance.registerSingletonList(TikTokEventListener.class, (e) -> listeners);
dependance.registerSingleton(ListenersManager.class, TikTokListenersManager.class); dependance.registerSingleton(ListenersManager.class, TikTokListenersManager.class);
//networking //networking
dependance.registerSingleton(HttpClientFactory.class); dependance.registerSingleton(HttpClientFactory.class);
dependance.registerSingleton(TikTokWebSocketPingingTask.class); dependance.registerSingleton(WebSocketHeartbeatTask.class);
if (clientSettings.isOffline()) { if (clientSettings.isOffline()) {
dependance.registerSingleton(SocketClient.class, TikTokWebSocketOfflineClient.class); dependance.registerSingleton(LiveSocketClient.class, TikTokWebSocketOfflineClient.class);
dependance.registerSingleton(LiveHttpClient.class, TikTokLiveHttpOfflineClient.class); dependance.registerSingleton(LiveHttpClient.class, TikTokLiveHttpOfflineClient.class);
} else { } else {
dependance.registerSingleton(SocketClient.class, TikTokWebSocketClient.class); dependance.registerSingleton(LiveSocketClient.class, TikTokWebSocketClient.class);
dependance.registerSingleton(LiveHttpClient.class, TikTokLiveHttpClient.class); dependance.registerSingleton(LiveHttpClient.class, TikTokLiveHttpClient.class);
} }
@@ -176,8 +182,10 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
dependance.registerSingleton(LiveClient.class, TikTokLiveClient.class); dependance.registerSingleton(LiveClient.class, TikTokLiveClient.class);
onCustomDependencies.forEach(action -> action.accept(dependance)); onCustomDependencies.forEach(action -> action.accept(dependance));
var container = dependance.build(); var container = dependance.build();
var listenerManager = container.find(ListenersManager.class);
listeners.forEach(listenerManager::addListener);
return container.find(LiveClient.class); return container.find(LiveClient.class);
} }

View File

@@ -136,7 +136,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient
var url = TIKTOK_URL_WEB + "api-live/user/room"; var url = TIKTOK_URL_WEB + "api-live/user/room";
var result = httpFactory.client(url) var result = httpFactory.client(url)
.withParam("uniqueId", request.getUserName()) .withParam("uniqueId", request.getUserName())
.withParam("sourceType", "54") .withParam("sourceType", "54") //MAGIC NUMBER, WHAT 54 means?
.build() .build()
.toJsonResponse(); .toJsonResponse();
@@ -203,7 +203,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient
} }
} }
private ActionResult<HttpResponse<byte[]>> getStartingPayload(LiveConnectionData.Request request) { protected ActionResult<HttpResponse<byte[]>> getStartingPayload(LiveConnectionData.Request request) {
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings(); var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
if (proxyClientSettings.isEnabled()) { if (proxyClientSettings.isEnabled()) {
while (proxyClientSettings.hasNext()) { while (proxyClientSettings.hasNext()) {
@@ -215,10 +215,10 @@ public class TikTokLiveHttpClient implements LiveHttpClient
return getByteResponse(request.getRoomId()); return getByteResponse(request.getRoomId());
} }
private ActionResult<HttpResponse<byte[]>> getByteResponse(String room_id) { protected ActionResult<HttpResponse<byte[]>> getByteResponse(String room_id) {
HttpClientBuilder builder = httpFactory.client(TIKTOK_SIGN_API) HttpClientBuilder builder = httpFactory.client(TIKTOK_SIGN_API)
.withParam("client", "ttlive-java") .withParam("client", "ttlive-java")
.withParam("uuc", "1") .withParam("uuc", "1") //MAGIC NUMBER!
.withParam("room_id", room_id); .withParam("room_id", room_id);
if (clientSettings.getApiKey() != null) if (clientSettings.getApiKey() != null)

View File

@@ -47,7 +47,7 @@ public class TikTokLiveHttpOfflineClient implements LiveHttpClient {
@Override @Override
public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) { public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) {
return new LiveUserData.Response("", LiveUserData.UserStatus.Live, "offline_room_id", 0); return new LiveUserData.Response("", LiveUserData.UserStatus.Live, "offline_room_id", 0, null);
} }
@Override @Override

View File

@@ -142,7 +142,7 @@ public class LiveDataMapper {
var link = pictureElement.getAsJsonArray("url_list").get(1).getAsString(); var link = pictureElement.getAsJsonArray("url_list").get(1).getAsString();
var picture = new Picture(link); var picture = new Picture(link);
var user = new User(id, name, profileName, picture, followers, followingCount, new ArrayList<>()); var user = new User(id, name, profileName, picture, followingCount, followers, new ArrayList<>());
user.addAttribute(UserAttribute.LiveHost); user.addAttribute(UserAttribute.LiveHost);
return user; return user;
} }

View File

@@ -23,9 +23,12 @@
package io.github.jwdeveloper.tiktok.http.mappers; package io.github.jwdeveloper.tiktok.http.mappers;
import com.google.gson.*; import com.google.gson.*;
import io.github.jwdeveloper.tiktok.data.models.Picture;
import io.github.jwdeveloper.tiktok.data.models.users.User;
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData; import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveRequestException;
import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
public class LiveUserDataMapper public class LiveUserDataMapper
@@ -40,17 +43,18 @@ public class LiveUserDataMapper
throw new TikTokLiveRequestException("fetchRoomIdFromTiktokApi -> Unable to fetch roomID, contact the developer"); throw new TikTokLiveRequestException("fetchRoomIdFromTiktokApi -> Unable to fetch roomID, contact the developer");
} }
if (message.equals("user_not_found")) { if (message.equals("user_not_found")) {
return new LiveUserData.Response(json, LiveUserData.UserStatus.NotFound, "", -1); return new LiveUserData.Response(json, LiveUserData.UserStatus.NotFound, "", -1, null);
} }
//live -> status 2 //live -> status 2
//live paused -> 3 //live paused -> 3
//not live -> status 4 //not live -> status 4
var element = jsonObject.get("data"); var element = jsonObject.get("data");
if (element.isJsonNull()) { if (element.isJsonNull()) {
return new LiveUserData.Response(json, LiveUserData.UserStatus.NotFound, "", -1); return new LiveUserData.Response(json, LiveUserData.UserStatus.NotFound, "", -1, null);
} }
var data = element.getAsJsonObject(); var data = element.getAsJsonObject();
var user = data.getAsJsonObject("user"); var user = data.getAsJsonObject("user");
var stats = data.getAsJsonObject("stats");
var roomId = user.get("roomId").getAsString(); var roomId = user.get("roomId").getAsString();
var status = user.get("status").getAsInt(); var status = user.get("status").getAsInt();
@@ -64,10 +68,19 @@ public class LiveUserDataMapper
default -> LiveUserData.UserStatus.NotFound; default -> LiveUserData.UserStatus.NotFound;
}; };
return new LiveUserData.Response(json, statusEnum, roomId, startTime); User foundUser = new User(
Long.parseLong(user.get("id").getAsString()),
user.get("uniqueId").getAsString(),
user.get("nickname").getAsString(),
new Picture(user.get("avatarLarger").getAsString()),
stats.get("followingCount").getAsLong(),
stats.get("followerCount").getAsLong(),
List.of());
return new LiveUserData.Response(json, statusEnum, roomId, startTime, foundUser);
} catch (JsonSyntaxException | IllegalStateException e) { } catch (JsonSyntaxException | IllegalStateException e) {
logger.warning("Malformed Json: '"+json+"' - Error Message: "+e.getMessage()); logger.warning("Malformed Json: '"+json+"' - Error Message: "+e.getMessage());
return new LiveUserData.Response(json, LiveUserData.UserStatus.NotFound, "", -1); return new LiveUserData.Response(json, LiveUserData.UserStatus.NotFound, "", -1, null);
} }
} }
} }

View File

@@ -22,19 +22,26 @@
*/ */
package io.github.jwdeveloper.tiktok.listener; package io.github.jwdeveloper.tiktok.listener;
import io.github.jwdeveloper.tiktok.annotations.Priority;
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer; import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
import lombok.Data;
import lombok.Value; import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
@Value @Data
public class ListenerBindingModel public class ListenerMethodInfo {
{ private Object listener;
TikTokEventListener listener; private Class eventType;
Map<Class<?>, List<EventConsumer<?>>> events; private Method method;
private Priority priority;
private boolean async;
private EventConsumer action = (a, b) -> {
};
} }

View File

@@ -23,104 +23,135 @@
package io.github.jwdeveloper.tiktok.listener; package io.github.jwdeveloper.tiktok.listener;
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler; import io.github.jwdeveloper.dependance.api.DependanceContainer;
import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver; 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.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokEventListenerMethodException; import io.github.jwdeveloper.tiktok.exceptions.TikTokEventListenerMethodException;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.live.LiveEventsHandler; import io.github.jwdeveloper.tiktok.live.LiveEventsHandler;
import io.github.jwdeveloper.tiktok.live.builder.EventConsumer; import io.github.jwdeveloper.tiktok.live.builder.EventConsumer;
import java.util.ArrayList; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.*;
import java.util.HashMap; import java.util.concurrent.ExecutorService;
import java.util.List; import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
public class TikTokListenersManager implements ListenersManager { public class TikTokListenersManager implements ListenersManager {
private final LiveEventsHandler eventObserver;
private final List<ListenerBindingModel> bindingModels;
public TikTokListenersManager(List<TikTokEventListener> listeners, LiveEventsHandler tikTokEventHandler) { private final Map<Object, List<ListenerMethodInfo>> listeners;
this.eventObserver = tikTokEventHandler; private final LiveEventsHandler eventsHandler;
this.bindingModels = new ArrayList<>(listeners.size()); private final ExecutorService executorService;
for (var listener : listeners) { private final DependanceContainer dependanceContainer;
addListener(listener);
}
public TikTokListenersManager(LiveEventsHandler tikTokEventHandler,
DependanceContainer dependanceContainer) {
this.eventsHandler = tikTokEventHandler;
this.dependanceContainer = dependanceContainer;
this.listeners = new HashMap<>();
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
} }
@Override @Override
public List<TikTokEventListener> getListeners() { public List<Object> getListeners() {
return bindingModels.stream().map(ListenerBindingModel::getListener).toList(); return listeners.keySet().stream().toList();
} }
@Override @Override
public void addListener(TikTokEventListener listener) { public void addListener(Object listener) {
var alreadyExists = bindingModels.stream().filter(e -> e.getListener() == listener).findAny(); if (listeners.containsKey(listener)) {
if (alreadyExists.isPresent()) {
throw new TikTokLiveException("Listener " + listener.getClass() + " has already been registered"); throw new TikTokLiveException("Listener " + listener.getClass() + " has already been registered");
} }
var bindingModel = bindToEvents(listener);
for (var eventEntrySet : bindingModel.getEvents().entrySet()) { var methodsInfo = getMethodsInfo(listener);
var eventType = eventEntrySet.getKey(); for (var methodInfo : methodsInfo) {
for (var methods : eventEntrySet.getValue()) { eventsHandler.subscribe(methodInfo.getEventType(), methodInfo.getAction());
eventObserver.subscribe(eventType, methods);
}
} }
bindingModels.add(bindingModel); listeners.put(listener, methodsInfo);
} }
@Override @Override
public void removeListener(TikTokEventListener listener) { public void removeListener(Object listener) {
var optional = bindingModels.stream().filter(e -> e.getListener() == listener).findAny(); if (!listeners.containsKey(listener)) {
if (optional.isEmpty()) {
return; return;
} }
var methodsInfo = listeners.get(listener);
var bindingModel = optional.get(); for (var methodInfo : methodsInfo) {
eventsHandler.unsubscribe(methodInfo.getEventType(), methodInfo.getAction());
for (var eventEntrySet : bindingModel.getEvents().entrySet()) {
var eventType = eventEntrySet.getKey();
for (var methods : eventEntrySet.getValue()) {
eventObserver.unsubscribe(eventType, methods);
}
} }
bindingModels.remove(optional.get()); listeners.remove(listener);
} }
private ListenerBindingModel bindToEvents(TikTokEventListener listener) { private List<ListenerMethodInfo> getMethodsInfo(Object listener) {
return Arrays.stream(listener.getClass().getDeclaredMethods())
.filter(e -> e.isAnnotationPresent(TikTokEventObserver.class))
.filter(e -> e.getParameterCount() >= 1)
.map(method -> getSingleMethodInfo(listener, method))
.sorted(Comparator.comparingInt(a -> a.getPriority().value))
.toList();
}
var clazz = listener.getClass(); private ListenerMethodInfo getSingleMethodInfo(Object listener, Method method) {
var methods = Arrays.stream(clazz.getDeclaredMethods()).filter(m ->
m.getParameterCount() == 2 &&
m.isAnnotationPresent(TikTokEventObserver.class)).toList();
var eventsMap = new HashMap<Class<?>, List<EventConsumer<?>>>();
for (var method : methods) {
var liveclientClass = method.getParameterTypes()[0];
var eventClass = method.getParameterTypes()[1];
if (!LiveClient.class.isAssignableFrom(liveclientClass) && !liveclientClass.equals(LiveClient.class)) { method.setAccessible(true);
throw new TikTokEventListenerMethodException("Method " + method.getName() + "() 1st parameter must be instance of " + LiveClient.class.getName() var annotation = method.getAnnotation(TikTokEventObserver.class);
+ " | Invalid parameter class: "+liveclientClass.getName()); var tiktokEventType = Arrays.stream(method.getParameterTypes())
} .filter(TikTokEvent.class::isAssignableFrom)
.findFirst()
.orElseThrow(() -> new TikTokEventListenerMethodException("Method " + method.getName() + "() must have only one parameter that inherits from class " + TikTokEvent.class.getName()));
if (!TikTokEvent.class.isAssignableFrom(eventClass) && !eventClass.equals(TikTokEvent.class)) { var info = new ListenerMethodInfo();
throw new TikTokEventListenerMethodException("Method " + method.getName() + "() 2nd parameter must be instance of " + TikTokEvent.class.getName() info.setListener(listener);
+ " | Invalid parameter class: "+eventClass.getName()); info.setAsync(annotation.async());
} info.setPriority(annotation.priority());
info.setEventType(tiktokEventType);
info.setAction(createAction(listener, method, tiktokEventType));
EventConsumer eventMethodRef = (liveClient, event) -> if (info.isAsync()) {
var action = info.getAction();
info.setAction((liveClient, event) ->
{ {
try { executorService.submit(() ->
method.setAccessible(true); {
method.invoke(listener, liveClient, event); action.onEvent(liveClient, event);
} catch (Exception e) { });
throw new TikTokEventListenerMethodException(e); });
}
};
eventsMap.computeIfAbsent(eventClass, (a) -> new ArrayList<>()).add(eventMethodRef);
} }
return new ListenerBindingModel(listener, eventsMap); return info;
}
//I know, implementation of this might look complicated
private EventConsumer createAction(Object listener, Method method, Class tiktokEventType) {
AtomicReference<Object> eventObjectRef = new AtomicReference<>();
var methodContainer = dependanceContainer.createChildContainer()
.configure(configuration ->
{
//Modifying container, so it returns TikTokEvent object instance,
//when TikTokEvent type is encountered in the methods parameters
configuration.onInjection(injectionEvent ->
{
if (injectionEvent.input().isAssignableFrom(tiktokEventType)) {
return eventObjectRef.get();
}
return injectionEvent.output();
});
})
.build();
return (liveClient, event) ->
{
try {
eventObjectRef.set(event);
//Creating list of input objects based on method parameters
//Objects are received from container
var parameters = methodContainer.resolveParameters(method);
method.invoke(listener, parameters);
} catch (Exception e) {
eventsHandler.publish(liveClient, new TikTokErrorEvent(new TikTokEventListenerMethodException(e)));
}
};
} }
} }

View File

@@ -53,7 +53,7 @@ public class TikTokGenericEventMapper {
private final Map<Class<?>, Method> methodCache; private final Map<Class<?>, Method> methodCache;
private final Map<TypePair, Constructor<?>> constructorCache; private final Map<TypePair, Constructor<?>> constructorCache;
private static final String PARSE_FIELD = "parseFrom";
public TikTokGenericEventMapper() { public TikTokGenericEventMapper() {
this.methodCache = new HashMap<>(); this.methodCache = new HashMap<>();
this.constructorCache = new HashMap<>(); this.constructorCache = new HashMap<>();
@@ -75,7 +75,7 @@ public class TikTokGenericEventMapper {
public Method getParsingMethod(Class<?> input) throws RuntimeException { public Method getParsingMethod(Class<?> input) throws RuntimeException {
return methodCache.computeIfAbsent(input, aClass -> { return methodCache.computeIfAbsent(input, aClass -> {
try { try {
return aClass.getDeclaredMethod("parseFrom", byte[].class); return aClass.getDeclaredMethod(PARSE_FIELD, byte[].class);
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -34,11 +34,12 @@ public class TikTokLiveMapper implements LiveMapper {
private final Map<String, TikTokLiveMapperModel> mappers; private final Map<String, TikTokLiveMapperModel> mappers;
private final LiveMapperHelper mapperUtils; private final LiveMapperHelper mapperUtils;
private final TikTokLiveMapperModel globalMapperModel; private final TikTokLiveMapperModel globalMapperModel;
private static final String GLOBAL_MESSAGE = "GLOBAL MESSAGE";
public TikTokLiveMapper(LiveMapperHelper mapperUtils) { public TikTokLiveMapper(LiveMapperHelper mapperUtils) {
this.mappers = new HashMap<>(); this.mappers = new HashMap<>();
this.mapperUtils = mapperUtils; this.mapperUtils = mapperUtils;
this.globalMapperModel = new TikTokLiveMapperModel("any message"); this.globalMapperModel = new TikTokLiveMapperModel(GLOBAL_MESSAGE);
} }
@Override @Override

View File

@@ -29,6 +29,9 @@ import io.github.jwdeveloper.tiktok.utils.ProtoBufferObject;
import io.github.jwdeveloper.tiktok.utils.ProtocolUtils; import io.github.jwdeveloper.tiktok.utils.ProtocolUtils;
public class TikTokLiveMapperHelper implements LiveMapperHelper { public class TikTokLiveMapperHelper implements LiveMapperHelper {
private static final String PACKAGE_PREFIX = "io.github.jwdeveloper.tiktok.messages.webcast.";
private final TikTokGenericEventMapper genericMapper; private final TikTokGenericEventMapper genericMapper;
public TikTokLiveMapperHelper(TikTokGenericEventMapper genericMapper) { public TikTokLiveMapperHelper(TikTokGenericEventMapper genericMapper) {
@@ -39,6 +42,7 @@ public class TikTokLiveMapperHelper implements LiveMapperHelper {
public <T extends GeneratedMessageV3> T bytesToWebcastObject(byte[] bytes, Class<T> messageClass) { public <T extends GeneratedMessageV3> T bytesToWebcastObject(byte[] bytes, Class<T> messageClass) {
try { try {
var parsingMethod = genericMapper.getParsingMethod(messageClass); var parsingMethod = genericMapper.getParsingMethod(messageClass);
//NULL is passed, since Parsing method is Static
var sourceObject = parsingMethod.invoke(null, bytes); var sourceObject = parsingMethod.invoke(null, bytes);
return (T) sourceObject; return (T) sourceObject;
} catch (Exception e) { } catch (Exception e) {
@@ -49,7 +53,7 @@ public class TikTokLiveMapperHelper implements LiveMapperHelper {
@Override @Override
public Object bytesToWebcastObject(byte[] bytes, String messageName) { public Object bytesToWebcastObject(byte[] bytes, String messageName) {
try { try {
var packageName = "io.github.jwdeveloper.tiktok.messages.webcast." + messageName; var packageName = PACKAGE_PREFIX + messageName;
var clazz = Class.forName(packageName); var clazz = Class.forName(packageName);
return bytesToWebcastObject(bytes, (Class<? extends GeneratedMessageV3>) clazz); return bytesToWebcastObject(bytes, (Class<? extends GeneratedMessageV3>) clazz);
} catch (Exception e) { } catch (Exception e) {
@@ -60,7 +64,7 @@ public class TikTokLiveMapperHelper implements LiveMapperHelper {
@Override @Override
public boolean isMessageHasProtoClass(String messageName) { public boolean isMessageHasProtoClass(String messageName) {
try { try {
var packageName = "io.github.jwdeveloper.tiktok.messages.webcast." + messageName; var packageName = PACKAGE_PREFIX + messageName;
Class.forName(packageName); Class.forName(packageName);
return true; return true;
} catch (Exception e) { } catch (Exception e) {

View File

@@ -38,7 +38,6 @@ import java.util.*;
public class TikTokGiftEventHandler { public class TikTokGiftEventHandler {
private final Map<Long, WebcastGiftMessage> giftsMessages; private final Map<Long, WebcastGiftMessage> giftsMessages;
private final TikTokRoomInfo tikTokRoomInfo; private final TikTokRoomInfo tikTokRoomInfo;
private final GiftsManager giftsManager; private final GiftsManager giftsManager;
public TikTokGiftEventHandler(GiftsManager giftsManager, TikTokRoomInfo tikTokRoomInfo) { public TikTokGiftEventHandler(GiftsManager giftsManager, TikTokRoomInfo tikTokRoomInfo) {

View File

@@ -22,8 +22,6 @@
*/ */
package io.github.jwdeveloper.tiktok.websocket; package io.github.jwdeveloper.tiktok.websocket;
import io.github.jwdeveloper.dependance.injector.api.containers.Container;
import io.github.jwdeveloper.tiktok.*;
import io.github.jwdeveloper.tiktok.data.dto.ProxyData; import io.github.jwdeveloper.tiktok.data.dto.ProxyData;
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData; import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
import io.github.jwdeveloper.tiktok.data.settings.*; import io.github.jwdeveloper.tiktok.data.settings.*;
@@ -38,11 +36,11 @@ import java.net.Proxy;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.HashMap; import java.util.HashMap;
public class TikTokWebSocketClient implements SocketClient { public class TikTokWebSocketClient implements LiveSocketClient {
private final LiveClientSettings clientSettings; private final LiveClientSettings clientSettings;
private final LiveMessagesHandler messageHandler; private final LiveMessagesHandler messageHandler;
private final LiveEventsHandler tikTokEventHandler; private final LiveEventsHandler tikTokEventHandler;
private final TikTokWebSocketPingingTask pingingTask; private final WebSocketHeartbeatTask heartbeatTask;
private WebSocketClient webSocketClient; private WebSocketClient webSocketClient;
private boolean isConnected; private boolean isConnected;
@@ -50,12 +48,12 @@ public class TikTokWebSocketClient implements SocketClient {
LiveClientSettings clientSettings, LiveClientSettings clientSettings,
LiveMessagesHandler messageHandler, LiveMessagesHandler messageHandler,
LiveEventsHandler tikTokEventHandler, LiveEventsHandler tikTokEventHandler,
TikTokWebSocketPingingTask pingingTask) WebSocketHeartbeatTask heartbeatTask)
{ {
this.clientSettings = clientSettings; this.clientSettings = clientSettings;
this.messageHandler = messageHandler; this.messageHandler = messageHandler;
this.tikTokEventHandler = tikTokEventHandler; this.tikTokEventHandler = tikTokEventHandler;
this.pingingTask = pingingTask; this.heartbeatTask = heartbeatTask;
isConnected = false; isConnected = false;
} }
@@ -86,7 +84,7 @@ public class TikTokWebSocketClient implements SocketClient {
private void connectDefault() { private void connectDefault() {
try { try {
webSocketClient.connect(); webSocketClient.connect();
pingingTask.run(webSocketClient, clientSettings.getPingInterval()); heartbeatTask.run(webSocketClient, clientSettings.getPingInterval());
isConnected = true; isConnected = true;
} catch (Exception e) { } catch (Exception e) {
isConnected = false; isConnected = false;
@@ -122,7 +120,7 @@ public class TikTokWebSocketClient implements SocketClient {
proxySettings.remove(); proxySettings.remove();
continue; continue;
} }
pingingTask.run(webSocketClient, clientSettings.getPingInterval()); heartbeatTask.run(webSocketClient, clientSettings.getPingInterval());
isConnected = true; isConnected = true;
break; break;
} }
@@ -143,7 +141,7 @@ public class TikTokWebSocketClient implements SocketClient {
public void stop() { public void stop() {
if (isConnected && webSocketClient != null && webSocketClient.isOpen()) { if (isConnected && webSocketClient != null && webSocketClient.isOpen()) {
webSocketClient.closeConnection(0, ""); webSocketClient.closeConnection(0, "");
pingingTask.stop(); heartbeatTask.stop();
} }
webSocketClient = null; webSocketClient = null;
isConnected = false; isConnected = false;

View File

@@ -23,7 +23,6 @@
package io.github.jwdeveloper.tiktok.websocket; package io.github.jwdeveloper.tiktok.websocket;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import io.github.jwdeveloper.tiktok.*;
import io.github.jwdeveloper.tiktok.data.events.*; import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.exceptions.TikTokProtocolBufferException; import io.github.jwdeveloper.tiktok.exceptions.TikTokProtocolBufferException;
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
@@ -40,9 +39,9 @@ import java.util.*;
public class TikTokWebSocketListener extends WebSocketClient { public class TikTokWebSocketListener extends WebSocketClient {
private final LiveMessagesHandler messageHandler; private final LiveMessagesHandler messagesHandler;
private final LiveEventsHandler tikTokEventHandler; private final LiveEventsHandler eventHandler;
private final LiveClient tikTokLiveClient; private final LiveClient liveClient;
public TikTokWebSocketListener(URI serverUri, public TikTokWebSocketListener(URI serverUri,
Map<String, String> httpHeaders, Map<String, String> httpHeaders,
@@ -51,9 +50,9 @@ public class TikTokWebSocketListener extends WebSocketClient {
LiveEventsHandler tikTokEventHandler, LiveEventsHandler tikTokEventHandler,
LiveClient tikTokLiveClient) { LiveClient tikTokLiveClient) {
super(serverUri, new Draft_6455(), httpHeaders, connectTimeout); super(serverUri, new Draft_6455(), httpHeaders, connectTimeout);
this.messageHandler = messageHandler; this.messagesHandler = messageHandler;
this.tikTokEventHandler = tikTokEventHandler; this.eventHandler = tikTokEventHandler;
this.tikTokLiveClient = tikTokLiveClient; this.liveClient = tikTokLiveClient;
} }
@Override @Override
@@ -61,7 +60,7 @@ public class TikTokWebSocketListener extends WebSocketClient {
try { try {
handleBinary(bytes.array()); handleBinary(bytes.array());
} catch (Exception e) { } catch (Exception e) {
tikTokEventHandler.publish(tikTokLiveClient, new TikTokErrorEvent(e)); eventHandler.publish(liveClient, new TikTokErrorEvent(e));
} }
if (isOpen()) { if (isOpen()) {
sendPing(); sendPing();
@@ -85,12 +84,12 @@ public class TikTokWebSocketListener extends WebSocketClient {
this.send(pushFrameBuilder.build().toByteArray()); this.send(pushFrameBuilder.build().toByteArray());
} }
} }
messageHandler.handle(tikTokLiveClient, webcastResponse); messagesHandler.handle(liveClient, webcastResponse);
} }
@Override @Override
public void onOpen(ServerHandshake serverHandshake) { public void onOpen(ServerHandshake serverHandshake) {
tikTokEventHandler.publish(tikTokLiveClient, new TikTokConnectedEvent()); eventHandler.publish(liveClient, new TikTokConnectedEvent());
if (isOpen()) { if (isOpen()) {
sendPing(); sendPing();
} }
@@ -98,13 +97,13 @@ public class TikTokWebSocketListener extends WebSocketClient {
@Override @Override
public void onClose(int code, String reason, boolean remote) { public void onClose(int code, String reason, boolean remote) {
tikTokEventHandler.publish(tikTokLiveClient, new TikTokDisconnectedEvent(reason)); eventHandler.publish(liveClient, new TikTokDisconnectedEvent(reason));
tikTokLiveClient.disconnect(); liveClient.disconnect();
} }
@Override @Override
public void onError(Exception error) { public void onError(Exception error) {
tikTokEventHandler.publish(tikTokLiveClient, new TikTokErrorEvent(error)); eventHandler.publish(liveClient, new TikTokErrorEvent(error));
if (isOpen()) { if (isOpen()) {
sendPing(); sendPing();
} }
@@ -132,6 +131,7 @@ public class TikTokWebSocketListener extends WebSocketClient {
@Override @Override
public void onMessage(String s) { public void onMessage(String s) {
// System.err.println(s); //TODO we are not using this method, however I wounder if there might be
//so messages that are send as String from TikTok, for example some Jsons
} }
} }

View File

@@ -22,14 +22,13 @@
*/ */
package io.github.jwdeveloper.tiktok.websocket; package io.github.jwdeveloper.tiktok.websocket;
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
import io.github.jwdeveloper.tiktok.data.events.TikTokConnectedEvent; import io.github.jwdeveloper.tiktok.data.events.TikTokConnectedEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent; import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData; import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.live.LiveEventsHandler; import io.github.jwdeveloper.tiktok.live.LiveEventsHandler;
public class TikTokWebSocketOfflineClient implements SocketClient { public class TikTokWebSocketOfflineClient implements LiveSocketClient {
private final LiveEventsHandler handler; private final LiveEventsHandler handler;
private LiveClient liveClient; private LiveClient liveClient;

View File

@@ -22,18 +22,19 @@
*/ */
package io.github.jwdeveloper.tiktok.websocket; package io.github.jwdeveloper.tiktok.websocket;
import io.github.jwdeveloper.tiktok.live.LiveEventsHandler;
import org.java_websocket.WebSocket; import org.java_websocket.WebSocket;
public class TikTokWebSocketPingingTask { public class WebSocketHeartbeatTask
{
private Thread thread; private Thread thread;
private boolean isRunning = false; private boolean isRunning = false;
private final int MAX_TIMEOUT = 250; private final int MAX_TIMEOUT = 250;
private final int SLEEP_TIME = 500; private final int SLEEP_TIME = 500;
private final byte[] heartbeatBytes = {58, 2, 104, 98}; // Byte Array of "3A026862" which is TikTok's custom heartbeat value
public void run(WebSocket webSocket, long pingTaskTime) { public void run(WebSocket webSocket, long pingTaskTime) {
stop(); stop();
thread = new Thread(() -> pingTask(webSocket, pingTaskTime), "pinging-task"); thread = new Thread(() -> heartbeatTask(webSocket, pingTaskTime), "heartbeat-task");
isRunning = true; isRunning = true;
thread.start(); thread.start();
} }
@@ -44,11 +45,11 @@ public class TikTokWebSocketPingingTask {
isRunning = false; isRunning = false;
} }
private void pingTask(WebSocket webSocket, long pingTaskTime) { private void heartbeatTask(WebSocket webSocket, long pingTaskTime) {
while (isRunning) { while (isRunning) {
try { try {
if (webSocket.isOpen()) { if (webSocket.isOpen()) {
webSocket.sendPing(); webSocket.send(heartbeatBytes);
Thread.sleep(pingTaskTime + (int) (Math.random() * MAX_TIMEOUT)); Thread.sleep(pingTaskTime + (int) (Math.random() * MAX_TIMEOUT));
} else } else
Thread.sleep(SLEEP_TIME); Thread.sleep(SLEEP_TIME);

View File

@@ -0,0 +1,49 @@
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.dependance.implementation.DependanceContainerBuilder;
import io.github.jwdeveloper.tiktok.data.events.TikTokConnectedEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent;
import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.data.events.control.TikTokConnectingEvent;
import io.github.jwdeveloper.tiktok.data.events.control.TikTokPreConnectionEvent;
import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
import io.github.jwdeveloper.tiktok.models.ConnectionState;
import org.junit.Assert;
import org.junit.Test;
public class TikTokLiveClientTests extends TikTokTestBase {
@Override
public void onBeforeEachTest(LiveClientBuilder liveClientBuilder,
DependanceContainerBuilder containerBuilder) {
}
@Test
public void shouldThrownWhenAlreadyConnected() {
roomInfoMock().setConnectionState(ConnectionState.CONNECTED);
Assert.assertThrows(TikTokLiveException.class, () ->
{
liveClient().connect();
});
Assert.assertEquals(ConnectionState.DISCONNECTED, roomInfoMock().getConnectionState());
AssertEvents(
TikTokErrorEvent.class,
TikTokDisconnectedEvent.class
);
}
@Test
public void shouldConnect() {
liveClient().connect();
Assert.assertEquals(ConnectionState.CONNECTED, roomInfoMock().getConnectionState());
AssertEvents(
TikTokConnectingEvent.class,
TikTokPreConnectionEvent.class,
TikTokConnectedEvent.class,
TikTokRoomInfoEvent.class);
}
}

View File

@@ -0,0 +1,53 @@
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.dependance.implementation.DependanceContainerBuilder;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.live.LiveEventsHandler;
import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder;
import io.github.jwdeveloper.tiktok.mocks.EventsHandlerMock;
import lombok.Getter;
import lombok.experimental.Accessors;
import org.junit.Before;
/**
* Base class for the unit tests
*/
@Getter
@Accessors(fluent = true)
public abstract class TikTokTestBase {
private LiveClient liveClient;
private EventsHandlerMock eventsHandlerMock;
private TikTokRoomInfo roomInfoMock;
public void AssertEvents(Class<? extends TikTokEvent>... events) {
eventsHandlerMock.assertEvents(events);
}
@Before
public void setup() {
var builder = TikTokLive.newClient("test");
eventsHandlerMock = new EventsHandlerMock();
roomInfoMock = new TikTokRoomInfo();
roomInfoMock.setHostName("test");
liveClient = builder
.configure(liveClientSettings ->
{
liveClientSettings.setOffline(true);
liveClientSettings.setFetchGifts(false);
})
.customize(containerBuilder ->
{
containerBuilder.registerSingleton(LiveEventsHandler.class, eventsHandlerMock);
containerBuilder.registerSingleton(TikTokRoomInfo.class, roomInfoMock);
onBeforeEachTest(builder, containerBuilder);
}).build();
}
public abstract void onBeforeEachTest(LiveClientBuilder liveClientBuilder, DependanceContainerBuilder containerBuilder);
}

View File

@@ -22,7 +22,10 @@
*/ */
package io.github.jwdeveloper.tiktok.listener; package io.github.jwdeveloper.tiktok.listener;
import io.github.jwdeveloper.dependance.Dependance;
import io.github.jwdeveloper.dependance.api.DependanceContainer;
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler; import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
import io.github.jwdeveloper.tiktok.annotations.Priority;
import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver; import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent; import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
@@ -44,27 +47,34 @@ class TikTokListenersManagerTest {
private TikTokLiveEventHandler eventObserver; private TikTokLiveEventHandler eventObserver;
private TikTokListenersManager tikTokListenersManager; private TikTokListenersManager tikTokListenersManager;
private DependanceContainer dependanceContainer;
private LiveClient liveClient;
@BeforeEach @BeforeEach
void setUp() { void setUp() {
eventObserver = Mockito.mock(TikTokLiveEventHandler.class);
List<TikTokEventListener> listeners = new ArrayList<>(); liveClient = Mockito.mock(LiveClient.class);
tikTokListenersManager = new TikTokListenersManager(listeners, eventObserver); eventObserver = new TikTokLiveEventHandler();
dependanceContainer = Dependance.newContainer()
.registerSingleton(LiveClient.class, liveClient)
.build();
tikTokListenersManager = new TikTokListenersManager(eventObserver, dependanceContainer);
} }
@Test @Test
void addListener() { void addListener() {
TikTokEventListener listener =new TikTokEventListenerTest(); Object listener = new TikTokEventListenerTest();
tikTokListenersManager.addListener(listener); tikTokListenersManager.addListener(listener);
List<TikTokEventListener> listeners = tikTokListenersManager.getListeners(); List<Object> listeners = tikTokListenersManager.getListeners();
assertEquals(1, listeners.size()); assertEquals(1, listeners.size());
assertSame(listener, listeners.get(0)); assertSame(listener, listeners.get(0));
} }
@Test @Test
void addListener_alreadyRegistered_throwsException() { void addListener_alreadyRegistered_throwsException() {
TikTokEventListener listener = new TikTokEventListenerTest(); Object listener = new TikTokEventListenerTest();
tikTokListenersManager.addListener(listener); tikTokListenersManager.addListener(listener);
Exception exception = assertThrows(TikTokLiveException.class, () -> { Exception exception = assertThrows(TikTokLiveException.class, () -> {
@@ -76,39 +86,56 @@ class TikTokListenersManagerTest {
@Test @Test
void removeListener() { void removeListener() {
TikTokEventListener listener = new TikTokEventListenerTest(); Object listener = new TikTokEventListenerTest();
tikTokListenersManager.addListener(listener); tikTokListenersManager.addListener(listener);
tikTokListenersManager.removeListener(listener); tikTokListenersManager.removeListener(listener);
List<TikTokEventListener> listeners = tikTokListenersManager.getListeners(); List<Object> listeners = tikTokListenersManager.getListeners();
assertTrue(listeners.isEmpty()); assertTrue(listeners.isEmpty());
} }
@Test
public void shouldTriggerEvents() {
Object listener = new TikTokEventListenerTest();
tikTokListenersManager.addListener(listener);
var fakeGiftEvent = TikTokGiftEvent.of("TestRosa", 1, 1);
eventObserver.publish(liveClient, fakeGiftEvent);
}
@Test @Test
void removeListener_notRegistered_doesNotThrow() { void removeListener_notRegistered_doesNotThrow() {
TikTokEventListener listener = new TikTokEventListenerTest(); Object listener = new TikTokEventListenerTest();
assertDoesNotThrow(() -> tikTokListenersManager.removeListener(listener)); assertDoesNotThrow(() -> tikTokListenersManager.removeListener(listener));
} }
public static class TikTokEventListenerTest implements TikTokEventListener public static class TikTokEventListenerTest {
{
@TikTokEventObserver @TikTokEventObserver
public void onJoin(LiveClient client, TikTokJoinEvent joinEvent) public void onJoin(LiveClient client, TikTokJoinEvent joinEvent) {
{ System.out.println("Hello from on join" + client + " " + joinEvent);
} }
@TikTokEventObserver @TikTokEventObserver(priority = Priority.LOWEST)
public void onGift(LiveClient client, TikTokGiftEvent giftMessageEvent) public void onGift(LiveClient client, TikTokGiftEvent giftMessageEvent) {
{ System.out.println("Hello from onGift lowest priority" + client + " " + giftMessageEvent);
} }
@TikTokEventObserver @TikTokEventObserver(priority = Priority.NORMAL)
public void onEvent(LiveClient client, TikTokEvent event) public void onGift2(LiveClient client, TikTokGiftEvent giftMessageEvent) {
{ System.out.println("Hello from onGift normal priority " + client + " " + giftMessageEvent);
}
@TikTokEventObserver(priority = Priority.HIGHEST)
public void onGift3(LiveClient client, TikTokGiftEvent giftMessageEvent) {
System.out.println("Hello from onGift highest priority " + client + " " + giftMessageEvent);
}
@TikTokEventObserver(async = true)
public void onEvent(LiveClient client, TikTokEvent event) {
System.out.println("Hello from onEvent im running on the thread " + Thread.currentThread().getName());
} }
} }
} }

View File

@@ -0,0 +1,46 @@
package io.github.jwdeveloper.tiktok.mocks;
import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.live.LiveClient;
import java.util.ArrayList;
import java.util.List;
/**
* Cache published events,
*/
public class EventsHandlerMock extends TikTokLiveEventHandler {
private final List<TikTokEvent> publishedEvents = new ArrayList<TikTokEvent>();
@Override
public void publish(LiveClient tikTokLiveClient, TikTokEvent tikTokEvent) {
super.publish(tikTokLiveClient, tikTokEvent);
publishedEvents.add(tikTokEvent);
}
@SafeVarargs
public final void assertEvents(Class<? extends TikTokEvent>... events) {
if (events.length == 0 && !publishedEvents.isEmpty()) {
var classNames = publishedEvents.stream()
.map(e -> e.getClass().getSimpleName())
.toList();
var invokedEvents = String.join("\n", classNames);
throw new IllegalArgumentException("Not events should be invoked but there was: \n" + invokedEvents);
}
for (var i = 0; i < events.length; i++) {
var expectedEvent = events[i];
var invokedEvent = publishedEvents.get(i);
if (expectedEvent.equals(invokedEvent.getClass())) {
continue;
}
throw new RuntimeException("Expected event was " + expectedEvent + " but acctuall was " + invokedEvent.getClass());
}
}
}

View File

@@ -70,7 +70,7 @@ Maven
<dependency> <dependency>
<groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId> <groupId>com.github.jwdeveloper.TikTok-Live-Java</groupId>
<artifactId>Client</artifactId> <artifactId>Client</artifactId>
<version>1.8.0-Release</version> <version>1.8.5-Release</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -87,7 +87,7 @@ dependencyResolutionManagement {
} }
dependencies { dependencies {
implementation 'com.github.jwdeveloper.TikTok-Live-Java:Client:1.5.0-Release' implementation 'com.github.jwdeveloper.TikTok-Live-Java:Client:1.8.5-Release'
} }
``` ```

View File

@@ -41,12 +41,12 @@
<parent> <parent>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<version>1.8.0-Release</version> <version>1.8.5-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>Examples</artifactId> <artifactId>examples</artifactId>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>

View File

@@ -37,9 +37,6 @@ public class ConnectionExample {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
showLogo(); showLogo();
var gifts = TikTokLive.gifts();
TikTokLive.newClient(ConnectionExample.TIKTOK_HOSTNAME) TikTokLive.newClient(ConnectionExample.TIKTOK_HOSTNAME)
.configure(clientSettings -> .configure(clientSettings ->
{ {

View File

@@ -30,7 +30,7 @@ public class CustomMappingExample {
public static void main(String[] args) { public static void main(String[] args) {
TikTokLive.newClient("saszareznikow") TikTokLive.newClient("saszareznikow")
.onMapping(mapper -> .mappings(mapper ->
{ {
mapper.forMessage(WebcastChatMessage.class) mapper.forMessage(WebcastChatMessage.class)
.onBeforeMapping((inputBytes, messageName, mapperHelper) -> .onBeforeMapping((inputBytes, messageName, mapperHelper) ->
@@ -56,7 +56,6 @@ public class CustomMappingExample {
System.out.println("onAfter mapping, " + source.getClass().getSimpleName() + " was mapped to " + events.size() + " events"); System.out.println("onAfter mapping, " + source.getClass().getSimpleName() + " was mapped to " + events.size() + " events");
return events; return events;
}); });
/* /*
There might be cast that we don't have Webcast class for incoming message from TikTok There might be cast that we don't have Webcast class for incoming message from TikTok
`mapperHelper.bytesToProtoBufferStructure` but you can still investigate message structure `mapperHelper.bytesToProtoBufferStructure` but you can still investigate message structure

View File

@@ -29,8 +29,6 @@ public class GiftsExample {
public static void main(String[] args) { public static void main(String[] args) {
var giftsManager = TikTokLive.gifts(); var giftsManager = TikTokLive.gifts();
var giftsList = giftsManager.toList(); var giftsList = giftsManager.toList();
for (var gift : giftsList) { for (var gift : giftsList) {
System.out.println("Gift: " + gift); System.out.println("Gift: " + gift);

View File

@@ -28,21 +28,18 @@ import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent;
import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent;
import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent; import io.github.jwdeveloper.tiktok.data.events.gift.TikTokGiftEvent;
import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent; import io.github.jwdeveloper.tiktok.data.events.social.TikTokLikeEvent;
import io.github.jwdeveloper.tiktok.listener.TikTokEventListener;
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.utils.ConsoleColors; import io.github.jwdeveloper.tiktok.utils.ConsoleColors;
import java.io.IOException; import java.io.IOException;
public class ListenerExample public class ListenerExample {
{
// <code> // <code>
/** /**
* * Listeners are an alternative way of handling events.
* Listeners are an alternative way of handling events. * I would to suggest to use then when logic of handing event
* I would to suggest to use then when logic of handing event * is more complex
* is more complex
*
*/ */
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
showLogo(); showLogo();
@@ -55,24 +52,21 @@ public class ListenerExample
} }
/** /**
* * Method must meet 2 requirements to be detected
* Method in TikTokEventListener should meet 4 requirements to be detected * - must have @TikTokEventObserver annotation
* - must have @TikTokEventObserver annotation * - must have 1 parameter of type that extending TikTokEvent
* - must have 2 parameters
* - first parameter must be LiveClient
* - second must be class that extending TikTokEvent
*/ */
public static class CustomListener implements TikTokEventListener { public static class CustomListener {
@TikTokEventObserver @TikTokEventObserver
public void onLike(LiveClient liveClient, TikTokLikeEvent event) { public void onLike(TikTokLikeEvent event) {
System.out.println(event.toString()); System.out.println(event.toString());
} }
@TikTokEventObserver @TikTokEventObserver
public void onError(LiveClient liveClient, TikTokErrorEvent event) { public void onError(TikTokErrorEvent event, LiveClient liveClient) {
// event.getException().printStackTrace(); // event.getException().printStackTrace();
} }
@TikTokEventObserver @TikTokEventObserver
@@ -103,9 +97,8 @@ public class ListenerExample
} }
// </code> // </code>
private static void showLogo() private static void showLogo() {
{ System.out.println(ConsoleColors.GREEN + """
System.out.println(ConsoleColors.GREEN+"""
_____ _ _ _____ _ _ _ \s _____ _ _ _____ _ _ _ \s
|_ _(_) | _|_ _|__ | | _| | (_)_ _____\s |_ _(_) | _|_ _|__ | | _| | (_)_ _____\s

View File

@@ -32,6 +32,7 @@ public class RecorderExample {
public static void main(String[] args) { public static void main(String[] args) {
TikTokLive.newClient("bangbetmenygy") TikTokLive.newClient("bangbetmenygy")
.configure(liveClientSettings -> .configure(liveClientSettings ->
{ {

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ package io.github.jwdeveloper.tiktok.extension.recorder.impl;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver; import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver;
import io.github.jwdeveloper.tiktok.data.events.*; import io.github.jwdeveloper.tiktok.data.events.*;
import io.github.jwdeveloper.tiktok.data.events.http.TikTokRoomDataResponseEvent; import io.github.jwdeveloper.tiktok.data.events.control.TikTokPreConnectionEvent;
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
import io.github.jwdeveloper.tiktok.extension.recorder.api.LiveRecorder; import io.github.jwdeveloper.tiktok.extension.recorder.api.LiveRecorder;
import io.github.jwdeveloper.tiktok.extension.recorder.impl.data.*; import io.github.jwdeveloper.tiktok.extension.recorder.impl.data.*;
@@ -34,28 +34,29 @@ import io.github.jwdeveloper.tiktok.extension.recorder.impl.event.TikTokLiveReco
import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.live.LiveClient;
import io.github.jwdeveloper.tiktok.models.ConnectionState; import io.github.jwdeveloper.tiktok.models.ConnectionState;
import javax.net.ssl.HttpsURLConnection;
import java.io.*; import java.io.*;
import java.net.URL; import java.net.URI;
import java.util.function.*; import java.net.http.*;
import java.time.Duration;
import java.util.function.BiConsumer;
public class RecorderListener implements LiveRecorder { public class RecorderListener implements LiveRecorder {
private final BiConsumer<RecorderSettings, LiveClient> consumer; private final BiConsumer<RecorderSettings, LiveClient> consumer;
private RecorderSettings settings; private final RecorderSettings settings;
private DownloadData downloadData; private DownloadData downloadData;
private Thread liveDownloadThread; private Thread liveDownloadThread;
public RecorderListener(BiConsumer<RecorderSettings, LiveClient> consumer) { public RecorderListener(BiConsumer<RecorderSettings, LiveClient> consumer) {
this.consumer = consumer; this.consumer = consumer;
this.settings = RecorderSettings.DEFAULT();
} }
@TikTokEventObserver @TikTokEventObserver
private void onResponse(LiveClient liveClient, TikTokRoomDataResponseEvent event) { private void onResponse(LiveClient liveClient, TikTokPreConnectionEvent event) {
settings = RecorderSettings.DEFAULT();
consumer.accept(settings, liveClient); consumer.accept(settings, liveClient);
var json = event.getLiveData().getJson(); var json = event.getUserData().getJson();
liveClient.getLogger().info("Searching for live download url"); liveClient.getLogger().info("Searching for live download url");
downloadData = settings.getPrepareDownloadData() != null ? downloadData = settings.getPrepareDownloadData() != null ?
@@ -70,26 +71,26 @@ public class RecorderListener implements LiveRecorder {
@TikTokEventObserver @TikTokEventObserver
private void onConnected(LiveClient liveClient, TikTokConnectedEvent event) { private void onConnected(LiveClient liveClient, TikTokConnectedEvent event) {
if (isConnected()) if (isConnected() || downloadData.getDownloadLiveUrl().isEmpty())
return; return;
liveDownloadThread = new Thread(() -> { liveDownloadThread = new Thread(() -> {
try { try {
liveClient.getLogger().info("Recording started "+liveClient.getRoomInfo().getHostName()); liveClient.getLogger().info("Recording started "+liveClient.getRoomInfo().getHostName());
var url = new URL(downloadData.getFullUrl());
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(URI.create(downloadData.getFullUrl())).GET();
var headers = LiveClientSettings.DefaultRequestHeaders(); for (var entry : LiveClientSettings.DefaultRequestHeaders().entrySet())
for (var entry : headers.entrySet()) { requestBuilder.header(entry.getKey(), entry.getValue());
connection.setRequestProperty(entry.getKey(), entry.getValue()); HttpResponse<InputStream> serverResponse = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL)
} .connectTimeout(Duration.ofSeconds(10)).build().send(requestBuilder.build(), HttpResponse.BodyHandlers.ofInputStream());
var file = settings.getOutputFile(); var file = settings.getOutputFile();
file.getParentFile().mkdirs(); file.getParentFile().mkdirs();
file.createNewFile(); file.createNewFile();
try ( try (
var in = connection.getInputStream(); var in = serverResponse.body();
var fos = new FileOutputStream(file) var fos = new FileOutputStream(file, true)
) { ) {
byte[] dataBuffer = new byte[1024]; byte[] dataBuffer = new byte[1024];
int bytesRead; int bytesRead;
@@ -128,35 +129,38 @@ public class RecorderListener implements LiveRecorder {
private DownloadData mapToDownloadData(String json) { private DownloadData mapToDownloadData(String json) {
try {
var parsedJson = JsonParser.parseString(json); var parsedJson = JsonParser.parseString(json);
var jsonObject = parsedJson.getAsJsonObject(); var jsonObject = parsedJson.getAsJsonObject();
var streamDataJson = jsonObject.getAsJsonObject("data") var streamDataJson = jsonObject.getAsJsonObject("data")
.getAsJsonObject("stream_url") .getAsJsonObject("liveRoom")
.getAsJsonObject("live_core_sdk_data") .getAsJsonObject("streamData")
.getAsJsonObject("pull_data") .getAsJsonObject("pull_data")
.get("stream_data") .get("stream_data")
.getAsString(); .getAsString();
var streamDataJsonObject = JsonParser.parseString(streamDataJson).getAsJsonObject(); var streamDataJsonObject = JsonParser.parseString(streamDataJson).getAsJsonObject();
var urlLink = streamDataJsonObject.getAsJsonObject("data") var urlLink = streamDataJsonObject.getAsJsonObject("data")
.getAsJsonObject(LiveQuality.origin.name()) .getAsJsonObject(LiveQuality.origin.name())
.getAsJsonObject("main") .getAsJsonObject("main")
.get("flv") .get("flv")
.getAsString(); .getAsString();
var sessionId = streamDataJsonObject.getAsJsonObject("common") var sessionId = streamDataJsonObject.getAsJsonObject("common")
.get("session_id") .get("session_id")
.getAsString(); .getAsString();
//main //main
//https://pull-f5-tt03.fcdn.eu.tiktokcdn.com/stage/stream-3284937501738533765.flv?session_id=136-20240109000954BF818F1B3A8E5E39E238&_webnoredir=1 //https://pull-f5-tt03.fcdn.eu.tiktokcdn.com/stage/stream-3284937501738533765.flv?session_id=136-20240109000954BF818F1B3A8E5E39E238&_webnoredir=1
//Working //Working
//https://pull-f5-tt03.fcdn.eu.tiktokcdn.com/game/stream-3284937501738533765_sd5.flv?_session_id=136-20240109001052D91FDBC00143211020C8.1704759052997&_webnoredir=1 //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 //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); return new DownloadData(urlLink, sessionId);
} catch (Exception e) {
return new DownloadData("", "");
}
} }
private boolean isConnected() { private boolean isConnected() {

View File

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

7
future.md Normal file
View File

@@ -0,0 +1,7 @@
Future plans:
[ ] Publish project to Maven repository instead of JITPACK
[ ] Dynamically generates readme.md when new version is released
[ ] Covers all possible classes from Client module with unit test
[ ] Add tests check to GitHub publish action
[ ] Generates documentation based on API module and publish it with GitHub pages

View File

@@ -7,11 +7,11 @@
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>1.8.0-Release</version> <version>1.8.5-Release</version>
<modules> <modules>
<module>API</module> <module>API</module>
<module>Client</module> <module>Client</module>
<module>Examples</module> <module>examples</module>
<module>tools-readme</module> <module>tools-readme</module>
<module>extension-recorder</module> <module>extension-recorder</module>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>TikTokLiveJava</artifactId> <artifactId>TikTokLiveJava</artifactId>
<groupId>io.github.jwdeveloper.tiktok</groupId> <groupId>io.github.jwdeveloper.tiktok</groupId>
<version>1.8.0-Release</version> <version>1.8.5-Release</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -34,12 +34,6 @@
<version>2.13.0</version> <version>2.13.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>com.github.jwdeveloper.Descrabble</groupId>
<artifactId>Descrabble-Full</artifactId>
<version>0.0.11-Release</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>org.reflections</groupId> <groupId>org.reflections</groupId>
<artifactId>reflections</artifactId> <artifactId>reflections</artifactId>

View File

@@ -1,17 +0,0 @@
package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.descrabble.api.DescriptionDecorator;
import io.github.jwdeveloper.descrabble.api.elements.Element;
import io.github.jwdeveloper.descrabble.api.elements.ElementFactory;
public class EventsDecorator implements DescriptionDecorator {
@Override
public void decorate(Element root, ElementFactory factory)
{
}
}

View File

@@ -41,7 +41,6 @@ public class EventsInfoGenerator {
System.out.println(res); System.out.println(res);
} }
public String run() { public String run() {
var events = getEventsDtos(); var events = getEventsDtos();
var builder = new StringBuilder(); var builder = new StringBuilder();

View File

@@ -1,8 +1,6 @@
package io.github.jwdeveloper.tiktok; package io.github.jwdeveloper.tiktok;
import io.github.jwdeveloper.descrabble.api.DescriptionGenerator;
import io.github.jwdeveloper.descrabble.framework.Descrabble;
import io.github.jwdeveloper.descrabble.plugin.github.DescrabbleGithub;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import java.io.File; import java.io.File;
@@ -23,16 +21,6 @@ public class Main
var output = System.getProperty("user.dir"); var output = System.getProperty("user.dir");
DescriptionGenerator generator = Descrabble.create()
.withTemplate(targetFile)
.withVariable("version", version)
.withDecorator(new EventsDecorator())
.withPlugin(DescrabbleGithub.plugin("README.md"))
.build();
generator.generate(output);
targetFile.delete();
inputStream.close();
} }
} }

View File

@@ -27,7 +27,6 @@ public class ReadmeGenerator {
public static void main(String[] args) { public static void main(String[] args) {
var generator = new ReadmeGenerator(); var generator = new ReadmeGenerator();
generator.generate(); generator.generate();
} }
public void generate() { public void generate() {

View File

@@ -88,8 +88,6 @@ Do you prefer other programming languages?
{{if item is 2}} {{if item is 2}}
my name is {{item.name}}
{{else}} {{else}}
{{end}} {{end}}
@@ -98,7 +96,6 @@ my name is {{item.name}}
<br> <br>
## Listeners ## Listeners
```java ```java
{{listener-content}} {{listener-content}}
``` ```