Skip to content

Commit 12c64e1

Browse files
authored
MINOR: Merge pull request #57 from jwdeveloper/develop-1.3.0
MINOR: Develop 1.3.0
2 parents 4f141ed + 5794ff2 commit 12c64e1

File tree

13 files changed

+78
-188
lines changed

13 files changed

+78
-188
lines changed

API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/SignServerResponse.java

Lines changed: 0 additions & 35 deletions
This file was deleted.

API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ public class LiveClientSettings {
8080
*/
8181
private String roomId;
8282

83+
/**
84+
* Optional: API Key for increased limit to signing server
85+
*/
86+
private String apiKey;
87+
8388
public static LiveClientSettings createDefault()
8489
{
8590
var httpSettings = new HttpClientSettings();

API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/ProxyClientSettings.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
@Setter
3434
public class ProxyClientSettings implements Iterator<ProxyData>
3535
{
36-
private boolean enabled, lastSuccess, autoDiscard = true, fallback = true;
36+
private boolean enabled, autoDiscard = true, fallback = true;
3737
private Rotation rotation = Rotation.CONSECUTIVE;
3838
private final List<ProxyData> proxyList = new ArrayList<>();
3939
private int index = -1;
@@ -63,10 +63,6 @@ public boolean hasNext() {
6363

6464
@Override
6565
public ProxyData next() {
66-
return lastSuccess ? proxyList.get(index) : rotate();
67-
}
68-
69-
public ProxyData rotate() {
7066
var nextProxy = switch (rotation)
7167
{
7268
case CONSECUTIVE -> {
@@ -84,12 +80,11 @@ public ProxyData rotate() {
8480
};
8581
onProxyUpdated.accept(nextProxy);
8682
return nextProxy;
87-
}
83+
}
8884

8985
@Override
9086
public void remove() {
9187
proxyList.remove(index);
92-
lastSuccess = false; // index is no longer valid and lastSuccess needs falsified
9388
}
9489

9590
public void setIndex(int index) {

API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,38 +27,41 @@
2727
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
2828
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
2929

30-
public interface LiveHttpClient {
31-
32-
30+
public interface LiveHttpClient
31+
{
3332
/**
3433
* @return list of gifts that are available in your country
3534
*/
3635
GiftsData.Response fetchGiftsData();
3736

3837
/**
3938
* Returns information about user that is having a livestream
40-
*
41-
* @param userName
42-
* @return
39+
* @param userName name of user
40+
* @return {@link LiveUserData.Response}
4341
*/
44-
LiveUserData.Response fetchLiveUserData(String userName);
42+
default LiveUserData.Response fetchLiveUserData(String userName) {
43+
return fetchLiveUserData(new LiveUserData.Request(userName));
44+
}
4545

4646
LiveUserData.Response fetchLiveUserData(LiveUserData.Request request);
4747

4848
/**
4949
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
50-
* @return
50+
* @return {@link LiveData.Response}
5151
*/
52-
LiveData.Response fetchLiveData(String roomId);
52+
default LiveData.Response fetchLiveData(String roomId) {
53+
return fetchLiveData(new LiveData.Request(roomId));
54+
}
5355

5456
LiveData.Response fetchLiveData(LiveData.Request request);
5557

56-
5758
/**
5859
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
59-
* @return
60+
* @return {@link LiveConnectionData.Response}
6061
*/
61-
LiveConnectionData.Response fetchLiveConnectionData(String roomId);
62+
default LiveConnectionData.Response fetchLiveConnectionData(String roomId) {
63+
return fetchLiveConnectionData(new LiveConnectionData.Request(roomId));
64+
}
6265

6366
LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request);
64-
}
67+
}

Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@
3232
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
3333
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
3434
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
35-
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
36-
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
35+
import io.github.jwdeveloper.tiktok.exceptions.*;
3736
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
3837
import io.github.jwdeveloper.tiktok.listener.ListenersManager;
3938
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
@@ -127,22 +126,26 @@ public void tryConnect() {
127126
var userData = httpClient.fetchLiveUserData(userDataRequest);
128127
liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp());
129128
liveRoomInfo.setRoomId(userData.getRoomId());
130-
if (userData.getUserStatus() == LiveUserData.UserStatus.Offline) {
131-
throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostUser());
132-
}
133-
if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound) {
134-
throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostUser());
135-
}
129+
130+
if (userData.getUserStatus() == LiveUserData.UserStatus.Offline)
131+
throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostName());
132+
133+
if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound)
134+
throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostName());
136135

137136
var liveDataRequest = new LiveData.Request(userData.getRoomId());
138137
var liveData = httpClient.fetchLiveData(liveDataRequest);
138+
139+
if (liveData.isAgeRestricted())
140+
throw new TikTokLiveException("Livestream for "+liveRoomInfo.getHostName()+" is 18+ or age restricted!");
141+
142+
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound)
143+
throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" could not be found.");
144+
145+
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline)
146+
throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" not found, is the Host offline?");
147+
139148
tikTokEventHandler.publish(this, new TikTokRoomDataResponseEvent(liveData));
140-
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) {
141-
throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found.");
142-
}
143-
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline) {
144-
throw new TikTokLiveOfflineHostException("LiveStream for not be found, is the Host offline?");
145-
}
146149

147150
liveRoomInfo.setTitle(liveData.getTitle());
148151
liveRoomInfo.setViewersCount(liveData.getViewers());

Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ public TikTokLiveClientBuilder configure(Consumer<LiveClientSettings> onConfigur
9999
}
100100

101101
public TikTokLiveClientBuilder addListener(TikTokEventListener listener) {
102-
listeners.add(listener);
102+
if (listener != null)
103+
listeners.add(listener);
103104
return this;
104105
}
105106

Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java

Lines changed: 16 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -36,26 +36,24 @@
3636
public class TikTokLiveHttpClient implements LiveHttpClient {
3737

3838
/**
39-
* Signing API by Isaac Kogan
40-
* https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures
41-
*/
42-
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/sign_url";
39+
* <a href="https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures">Signing API by Isaac Kogan</a>
40+
*/
41+
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch";
4342
private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/";
4443
private static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/";
44+
public static final int TIKTOK_AGE_RESTRICTED_CODE = 4003110;
4545

4646
private final HttpClientFactory httpFactory;
4747
private final LiveClientSettings clientSettings;
4848
private final LiveUserDataMapper liveUserDataMapper;
4949
private final LiveDataMapper liveDataMapper;
50-
private final SignServerResponseMapper signServerResponseMapper;
5150
private final GiftsDataMapper giftsDataMapper;
5251

5352
public TikTokLiveHttpClient(HttpClientFactory factory, LiveClientSettings settings) {
5453
this.httpFactory = factory;
5554
clientSettings = settings;
5655
liveUserDataMapper = new LiveUserDataMapper();
5756
liveDataMapper = new LiveDataMapper();
58-
signServerResponseMapper = new SignServerResponseMapper();
5957
giftsDataMapper = new GiftsDataMapper();
6058
}
6159

@@ -94,12 +92,6 @@ public GiftsData.Response fetchGiftsData() {
9492
return giftsDataMapper.map(json);
9593
}
9694

97-
98-
@Override
99-
public LiveUserData.Response fetchLiveUserData(String userName) {
100-
return fetchLiveUserData(new LiveUserData.Request(userName));
101-
}
102-
10395
@Override
10496
public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) {
10597
var url = TIKTOK_URL_WEB + "api-live/user/room";
@@ -136,11 +128,6 @@ public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) {
136128
return liveUserDataMapper.map(json);
137129
}
138130

139-
@Override
140-
public LiveData.Response fetchLiveData(String roomId) {
141-
return fetchLiveData(new LiveData.Request(roomId));
142-
}
143-
144131
@Override
145132
public LiveData.Response fetchLiveData(LiveData.Request request) {
146133
var url = TIKTOK_URL_WEBCAST + "room/info";
@@ -175,20 +162,12 @@ public LiveData.Response fetchLiveData(LiveData.Request request) {
175162
return liveDataMapper.map(json);
176163
}
177164

178-
@Override
179-
public LiveConnectionData.Response fetchLiveConnectionData(String roomId) {
180-
return fetchLiveConnectionData(new LiveConnectionData.Request(roomId));
181-
}
182-
183165
@Override
184166
public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) {
185-
HttpResponse<byte[]> credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> {
186-
SignServerResponse signServerResponse = getSignedUrl(request.getRoomId());
187-
return getWebsocketCredentialsResponse(signServerResponse.getSignedUrl());
188-
});
167+
HttpResponse<byte[]> credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> getStarterPayload(request.getRoomId()));
189168

190169
try {
191-
var optionalHeader = credentialsResponse.headers().firstValue("set-cookie");
170+
var optionalHeader = credentialsResponse.headers().firstValue("x-set-tt-cookie");
192171
if (optionalHeader.isEmpty()) {
193172
throw new TikTokSignServerException("Sign server did not return the set-cookie header");
194173
}
@@ -210,49 +189,29 @@ public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Re
210189
}
211190
}
212191

213-
SignServerResponse getSignedUrl(String roomId) {
214-
var urlToSign = httpFactory
215-
.client(TikTokLiveHttpClient.TIKTOK_URL_WEBCAST + "im/fetch")
216-
.withParam("room_id", roomId)
217-
.build()
218-
.toUrl();
192+
HttpResponse<byte[]> getStarterPayload(String room_id) {
193+
HttpClientBuilder builder = httpFactory.client(TIKTOK_SIGN_API)
194+
.withParam("client", "ttlive-java")
195+
.withParam("uuc", "1")
196+
.withParam("room_id", room_id);
219197

198+
if (clientSettings.getApiKey() != null)
199+
builder.withParam("apiKey", clientSettings.getApiKey());
220200

221-
var optional = httpFactory
222-
.client(TikTokLiveHttpClient.TIKTOK_SIGN_API)
223-
.withParam("client", "ttlive-java")
224-
.withParam("uuc", "1")
225-
.withParam("url", urlToSign.toString())
226-
.build()
227-
.toJsonResponse();
201+
var optional = builder.build().toResponse();
228202

229203
if (optional.isEmpty()) {
230-
throw new TikTokSignServerException("Unable to sign url: " + urlToSign);
231-
}
232-
233-
var json = optional.get();
234-
return signServerResponseMapper.map(json);
235-
}
236-
237-
HttpResponse<byte[]> getWebsocketCredentialsResponse(String signedUrl) {
238-
var optionalResponse = httpFactory
239-
.clientEmpty(signedUrl)
240-
.build()
241-
.toResponse();
242-
if (optionalResponse.isEmpty()) {
243204
throw new TikTokSignServerException("Unable to get websocket connection credentials");
244205
}
245-
return optionalResponse.get();
206+
return optional.get();
246207
}
247208

248209
Optional<HttpResponse<byte[]>> getOptionalProxyResponse(LiveConnectionData.Request request) {
249210
var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings();
250211
if (proxyClientSettings.isEnabled()) {
251212
while (proxyClientSettings.hasNext()) {
252213
try {
253-
SignServerResponse signServerResponse = getSignedUrl(request.getRoomId());
254-
HttpResponse<byte[]> credentialsResponse = getWebsocketCredentialsResponse(signServerResponse.getSignedUrl());
255-
clientSettings.getHttpSettings().getProxyClientSettings().rotate();
214+
HttpResponse<byte[]> credentialsResponse = getStarterPayload(request.getRoomId());
256215
return Optional.of(credentialsResponse);
257216
} catch (TikTokProxyRequestException | TikTokSignServerException ignored) {}
258217
}

Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,12 @@ public Optional<HttpResponse<byte[]>> handleHttpProxyRequest() {
6767
var request = prepareGetRequest();
6868

6969
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
70-
if (response.statusCode() != 200) {
71-
proxySettings.setLastSuccess(false);
70+
if (response.statusCode() != 200)
7271
continue;
73-
}
74-
proxySettings.setLastSuccess(true);
7572
return Optional.of(response);
7673
} catch (HttpConnectTimeoutException | ConnectException e) {
7774
if (proxySettings.isAutoDiscard())
7875
proxySettings.remove();
79-
proxySettings.setLastSuccess(false);
8076
throw new TikTokProxyRequestException(e);
8177
} catch (IOException e) {
8278
if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported
@@ -121,14 +117,12 @@ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {}
121117

122118
var response = createHttpResponse(body, toUrl(), responseInfo);
123119

124-
proxySettings.setLastSuccess(true);
125120
return Optional.of(response);
126121
} catch (IOException e) {
127122
if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported
128123
return super.toResponse();
129124
if (proxySettings.isAutoDiscard())
130125
proxySettings.remove();
131-
proxySettings.setLastSuccess(false);
132126
throw new TikTokProxyRequestException(e);
133127
} catch (Exception e) {
134128
throw new TikTokLiveRequestException(e);
@@ -137,7 +131,7 @@ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {}
137131
throw new TikTokLiveRequestException("No more proxies available!");
138132
} catch (NoSuchAlgorithmException | MalformedURLException | KeyManagementException e) {
139133
// Should never be reached!
140-
System.out.println("handleSocksProxyRequest()! If you see this message, reach us on discord!");
134+
System.out.println("handleSocksProxyRequest: If you see this, message us on discord!");
141135
e.printStackTrace();
142136
return Optional.empty();
143137
} catch (TikTokLiveRequestException e) {

Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import com.google.gson.JsonObject;
2626
import com.google.gson.JsonParser;
27+
import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient;
2728
import io.github.jwdeveloper.tiktok.data.models.Picture;
2829
import io.github.jwdeveloper.tiktok.data.models.users.User;
2930
import io.github.jwdeveloper.tiktok.data.models.users.UserAttribute;
@@ -64,6 +65,9 @@ public LiveData.Response map(String json) {
6465
default -> LiveData.LiveStatus.HostNotFound;
6566
};
6667
response.setLiveStatus(statusValue);
68+
} else if (data.has("prompts") && jsonObject.has("status_code") &&
69+
data.get("prompts").getAsString().isEmpty() && jsonObject.get("status_code").isJsonPrimitive()) {
70+
response.setAgeRestricted(jsonObject.get("status_code").getAsInt() == TikTokLiveHttpClient.TIKTOK_AGE_RESTRICTED_CODE);
6771
} else {
6872
response.setLiveStatus(LiveData.LiveStatus.HostNotFound);
6973
}

0 commit comments

Comments
 (0)