mirror of
https://github.com/jwdeveloper/TikTokLiveJava.git
synced 2026-02-27 08:49:40 -05:00
MINOR: Merge pull request #57 from jwdeveloper/develop-1.3.0
MINOR: Develop 1.3.0
This commit is contained in:
@@ -1,35 +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.requests;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class SignServerResponse
|
|
||||||
{
|
|
||||||
private String signedUrl;
|
|
||||||
|
|
||||||
private String userAgent;
|
|
||||||
}
|
|
||||||
@@ -80,6 +80,11 @@ public class LiveClientSettings {
|
|||||||
*/
|
*/
|
||||||
private String roomId;
|
private String roomId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional: API Key for increased limit to signing server
|
||||||
|
*/
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
public static LiveClientSettings createDefault()
|
public static LiveClientSettings createDefault()
|
||||||
{
|
{
|
||||||
var httpSettings = new HttpClientSettings();
|
var httpSettings = new HttpClientSettings();
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import java.util.function.Consumer;
|
|||||||
@Setter
|
@Setter
|
||||||
public class ProxyClientSettings implements Iterator<ProxyData>
|
public class ProxyClientSettings implements Iterator<ProxyData>
|
||||||
{
|
{
|
||||||
private boolean enabled, lastSuccess, autoDiscard = true, fallback = true;
|
private boolean enabled, autoDiscard = true, fallback = true;
|
||||||
private Rotation rotation = Rotation.CONSECUTIVE;
|
private Rotation rotation = Rotation.CONSECUTIVE;
|
||||||
private final List<ProxyData> proxyList = new ArrayList<>();
|
private final List<ProxyData> proxyList = new ArrayList<>();
|
||||||
private int index = -1;
|
private int index = -1;
|
||||||
@@ -63,10 +63,6 @@ public class ProxyClientSettings implements Iterator<ProxyData>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProxyData next() {
|
public ProxyData next() {
|
||||||
return lastSuccess ? proxyList.get(index) : rotate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProxyData rotate() {
|
|
||||||
var nextProxy = switch (rotation)
|
var nextProxy = switch (rotation)
|
||||||
{
|
{
|
||||||
case CONSECUTIVE -> {
|
case CONSECUTIVE -> {
|
||||||
@@ -84,12 +80,11 @@ public class ProxyClientSettings implements Iterator<ProxyData>
|
|||||||
};
|
};
|
||||||
onProxyUpdated.accept(nextProxy);
|
onProxyUpdated.accept(nextProxy);
|
||||||
return nextProxy;
|
return nextProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove() {
|
public void remove() {
|
||||||
proxyList.remove(index);
|
proxyList.remove(index);
|
||||||
lastSuccess = false; // index is no longer valid and lastSuccess needs falsified
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIndex(int index) {
|
public void setIndex(int index) {
|
||||||
|
|||||||
@@ -27,9 +27,8 @@ import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
|
|||||||
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
|
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
|
||||||
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
|
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
|
||||||
|
|
||||||
public interface LiveHttpClient {
|
public interface LiveHttpClient
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return list of gifts that are available in your country
|
* @return list of gifts that are available in your country
|
||||||
*/
|
*/
|
||||||
@@ -37,28 +36,32 @@ public interface LiveHttpClient {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns information about user that is having a livestream
|
* Returns information about user that is having a livestream
|
||||||
*
|
* @param userName name of user
|
||||||
* @param userName
|
* @return {@link LiveUserData.Response}
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
LiveUserData.Response fetchLiveUserData(String userName);
|
default LiveUserData.Response fetchLiveUserData(String userName) {
|
||||||
|
return fetchLiveUserData(new LiveUserData.Request(userName));
|
||||||
|
}
|
||||||
|
|
||||||
LiveUserData.Response fetchLiveUserData(LiveUserData.Request request);
|
LiveUserData.Response fetchLiveUserData(LiveUserData.Request request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
|
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
|
||||||
* @return
|
* @return {@link LiveData.Response}
|
||||||
*/
|
*/
|
||||||
LiveData.Response fetchLiveData(String roomId);
|
default LiveData.Response fetchLiveData(String roomId) {
|
||||||
|
return fetchLiveData(new LiveData.Request(roomId));
|
||||||
|
}
|
||||||
|
|
||||||
LiveData.Response fetchLiveData(LiveData.Request request);
|
LiveData.Response fetchLiveData(LiveData.Request request);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
|
* @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData
|
||||||
* @return
|
* @return {@link LiveConnectionData.Response}
|
||||||
*/
|
*/
|
||||||
LiveConnectionData.Response fetchLiveConnectionData(String roomId);
|
default LiveConnectionData.Response fetchLiveConnectionData(String roomId) {
|
||||||
|
return fetchLiveConnectionData(new LiveConnectionData.Request(roomId));
|
||||||
|
}
|
||||||
|
|
||||||
LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request);
|
LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request);
|
||||||
}
|
}
|
||||||
@@ -32,8 +32,7 @@ import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent;
|
|||||||
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
|
import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData;
|
||||||
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
|
import io.github.jwdeveloper.tiktok.data.requests.LiveData;
|
||||||
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
|
import io.github.jwdeveloper.tiktok.data.requests.LiveUserData;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
import io.github.jwdeveloper.tiktok.exceptions.*;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException;
|
|
||||||
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager;
|
||||||
import io.github.jwdeveloper.tiktok.listener.ListenersManager;
|
import io.github.jwdeveloper.tiktok.listener.ListenersManager;
|
||||||
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager;
|
||||||
@@ -127,22 +126,26 @@ public class TikTokLiveClient implements LiveClient {
|
|||||||
var userData = httpClient.fetchLiveUserData(userDataRequest);
|
var userData = httpClient.fetchLiveUserData(userDataRequest);
|
||||||
liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp());
|
liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp());
|
||||||
liveRoomInfo.setRoomId(userData.getRoomId());
|
liveRoomInfo.setRoomId(userData.getRoomId());
|
||||||
if (userData.getUserStatus() == LiveUserData.UserStatus.Offline) {
|
|
||||||
throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostUser());
|
if (userData.getUserStatus() == LiveUserData.UserStatus.Offline)
|
||||||
}
|
throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostName());
|
||||||
if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound) {
|
|
||||||
throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostUser());
|
if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound)
|
||||||
}
|
throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostName());
|
||||||
|
|
||||||
var liveDataRequest = new LiveData.Request(userData.getRoomId());
|
var liveDataRequest = new LiveData.Request(userData.getRoomId());
|
||||||
var liveData = httpClient.fetchLiveData(liveDataRequest);
|
var liveData = httpClient.fetchLiveData(liveDataRequest);
|
||||||
|
|
||||||
|
if (liveData.isAgeRestricted())
|
||||||
|
throw new TikTokLiveException("Livestream for "+liveRoomInfo.getHostName()+" is 18+ or age restricted!");
|
||||||
|
|
||||||
|
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound)
|
||||||
|
throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" could not be found.");
|
||||||
|
|
||||||
|
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline)
|
||||||
|
throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" not found, is the Host offline?");
|
||||||
|
|
||||||
tikTokEventHandler.publish(this, new TikTokRoomDataResponseEvent(liveData));
|
tikTokEventHandler.publish(this, new TikTokRoomDataResponseEvent(liveData));
|
||||||
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) {
|
|
||||||
throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found.");
|
|
||||||
}
|
|
||||||
if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline) {
|
|
||||||
throw new TikTokLiveOfflineHostException("LiveStream for not be found, is the Host offline?");
|
|
||||||
}
|
|
||||||
|
|
||||||
liveRoomInfo.setTitle(liveData.getTitle());
|
liveRoomInfo.setTitle(liveData.getTitle());
|
||||||
liveRoomInfo.setViewersCount(liveData.getViewers());
|
liveRoomInfo.setViewersCount(liveData.getViewers());
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TikTokLiveClientBuilder addListener(TikTokEventListener listener) {
|
public TikTokLiveClientBuilder addListener(TikTokEventListener listener) {
|
||||||
listeners.add(listener);
|
if (listener != null)
|
||||||
|
listeners.add(listener);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,18 +36,17 @@ import java.util.Optional;
|
|||||||
public class TikTokLiveHttpClient implements LiveHttpClient {
|
public class TikTokLiveHttpClient implements LiveHttpClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signing API by Isaac Kogan
|
* <a href="https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures">Signing API by Isaac Kogan</a>
|
||||||
* https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures
|
*/
|
||||||
*/
|
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch";
|
||||||
private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/sign_url";
|
|
||||||
private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/";
|
private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/";
|
||||||
private static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/";
|
private static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/";
|
||||||
|
public static final int TIKTOK_AGE_RESTRICTED_CODE = 4003110;
|
||||||
|
|
||||||
private final HttpClientFactory httpFactory;
|
private final HttpClientFactory httpFactory;
|
||||||
private final LiveClientSettings clientSettings;
|
private final LiveClientSettings clientSettings;
|
||||||
private final LiveUserDataMapper liveUserDataMapper;
|
private final LiveUserDataMapper liveUserDataMapper;
|
||||||
private final LiveDataMapper liveDataMapper;
|
private final LiveDataMapper liveDataMapper;
|
||||||
private final SignServerResponseMapper signServerResponseMapper;
|
|
||||||
private final GiftsDataMapper giftsDataMapper;
|
private final GiftsDataMapper giftsDataMapper;
|
||||||
|
|
||||||
public TikTokLiveHttpClient(HttpClientFactory factory, LiveClientSettings settings) {
|
public TikTokLiveHttpClient(HttpClientFactory factory, LiveClientSettings settings) {
|
||||||
@@ -55,7 +54,6 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
|
|||||||
clientSettings = settings;
|
clientSettings = settings;
|
||||||
liveUserDataMapper = new LiveUserDataMapper();
|
liveUserDataMapper = new LiveUserDataMapper();
|
||||||
liveDataMapper = new LiveDataMapper();
|
liveDataMapper = new LiveDataMapper();
|
||||||
signServerResponseMapper = new SignServerResponseMapper();
|
|
||||||
giftsDataMapper = new GiftsDataMapper();
|
giftsDataMapper = new GiftsDataMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,12 +92,6 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
|
|||||||
return giftsDataMapper.map(json);
|
return giftsDataMapper.map(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LiveUserData.Response fetchLiveUserData(String userName) {
|
|
||||||
return fetchLiveUserData(new LiveUserData.Request(userName));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) {
|
public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) {
|
||||||
var url = TIKTOK_URL_WEB + "api-live/user/room";
|
var url = TIKTOK_URL_WEB + "api-live/user/room";
|
||||||
@@ -136,11 +128,6 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
|
|||||||
return liveUserDataMapper.map(json);
|
return liveUserDataMapper.map(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public LiveData.Response fetchLiveData(String roomId) {
|
|
||||||
return fetchLiveData(new LiveData.Request(roomId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LiveData.Response fetchLiveData(LiveData.Request request) {
|
public LiveData.Response fetchLiveData(LiveData.Request request) {
|
||||||
var url = TIKTOK_URL_WEBCAST + "room/info";
|
var url = TIKTOK_URL_WEBCAST + "room/info";
|
||||||
@@ -175,20 +162,12 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
|
|||||||
return liveDataMapper.map(json);
|
return liveDataMapper.map(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public LiveConnectionData.Response fetchLiveConnectionData(String roomId) {
|
|
||||||
return fetchLiveConnectionData(new LiveConnectionData.Request(roomId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) {
|
public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) {
|
||||||
HttpResponse<byte[]> credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> {
|
HttpResponse<byte[]> credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> getStarterPayload(request.getRoomId()));
|
||||||
SignServerResponse signServerResponse = getSignedUrl(request.getRoomId());
|
|
||||||
return getWebsocketCredentialsResponse(signServerResponse.getSignedUrl());
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var optionalHeader = credentialsResponse.headers().firstValue("set-cookie");
|
var optionalHeader = credentialsResponse.headers().firstValue("x-set-tt-cookie");
|
||||||
if (optionalHeader.isEmpty()) {
|
if (optionalHeader.isEmpty()) {
|
||||||
throw new TikTokSignServerException("Sign server did not return the set-cookie header");
|
throw new TikTokSignServerException("Sign server did not return the set-cookie header");
|
||||||
}
|
}
|
||||||
@@ -210,39 +189,21 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SignServerResponse getSignedUrl(String roomId) {
|
HttpResponse<byte[]> getStarterPayload(String room_id) {
|
||||||
var urlToSign = httpFactory
|
HttpClientBuilder builder = httpFactory.client(TIKTOK_SIGN_API)
|
||||||
.client(TikTokLiveHttpClient.TIKTOK_URL_WEBCAST + "im/fetch")
|
.withParam("client", "ttlive-java")
|
||||||
.withParam("room_id", roomId)
|
.withParam("uuc", "1")
|
||||||
.build()
|
.withParam("room_id", room_id);
|
||||||
.toUrl();
|
|
||||||
|
|
||||||
|
if (clientSettings.getApiKey() != null)
|
||||||
|
builder.withParam("apiKey", clientSettings.getApiKey());
|
||||||
|
|
||||||
var optional = httpFactory
|
var optional = builder.build().toResponse();
|
||||||
.client(TikTokLiveHttpClient.TIKTOK_SIGN_API)
|
|
||||||
.withParam("client", "ttlive-java")
|
|
||||||
.withParam("uuc", "1")
|
|
||||||
.withParam("url", urlToSign.toString())
|
|
||||||
.build()
|
|
||||||
.toJsonResponse();
|
|
||||||
|
|
||||||
if (optional.isEmpty()) {
|
if (optional.isEmpty()) {
|
||||||
throw new TikTokSignServerException("Unable to sign url: " + urlToSign);
|
|
||||||
}
|
|
||||||
|
|
||||||
var json = optional.get();
|
|
||||||
return signServerResponseMapper.map(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpResponse<byte[]> getWebsocketCredentialsResponse(String signedUrl) {
|
|
||||||
var optionalResponse = httpFactory
|
|
||||||
.clientEmpty(signedUrl)
|
|
||||||
.build()
|
|
||||||
.toResponse();
|
|
||||||
if (optionalResponse.isEmpty()) {
|
|
||||||
throw new TikTokSignServerException("Unable to get websocket connection credentials");
|
throw new TikTokSignServerException("Unable to get websocket connection credentials");
|
||||||
}
|
}
|
||||||
return optionalResponse.get();
|
return optional.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<HttpResponse<byte[]>> getOptionalProxyResponse(LiveConnectionData.Request request) {
|
Optional<HttpResponse<byte[]>> getOptionalProxyResponse(LiveConnectionData.Request request) {
|
||||||
@@ -250,9 +211,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient {
|
|||||||
if (proxyClientSettings.isEnabled()) {
|
if (proxyClientSettings.isEnabled()) {
|
||||||
while (proxyClientSettings.hasNext()) {
|
while (proxyClientSettings.hasNext()) {
|
||||||
try {
|
try {
|
||||||
SignServerResponse signServerResponse = getSignedUrl(request.getRoomId());
|
HttpResponse<byte[]> credentialsResponse = getStarterPayload(request.getRoomId());
|
||||||
HttpResponse<byte[]> credentialsResponse = getWebsocketCredentialsResponse(signServerResponse.getSignedUrl());
|
|
||||||
clientSettings.getHttpSettings().getProxyClientSettings().rotate();
|
|
||||||
return Optional.of(credentialsResponse);
|
return Optional.of(credentialsResponse);
|
||||||
} catch (TikTokProxyRequestException | TikTokSignServerException ignored) {}
|
} catch (TikTokProxyRequestException | TikTokSignServerException ignored) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,16 +67,12 @@ public class HttpProxyClient extends HttpClient
|
|||||||
var request = prepareGetRequest();
|
var request = prepareGetRequest();
|
||||||
|
|
||||||
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
|
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
|
||||||
if (response.statusCode() != 200) {
|
if (response.statusCode() != 200)
|
||||||
proxySettings.setLastSuccess(false);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
proxySettings.setLastSuccess(true);
|
|
||||||
return Optional.of(response);
|
return Optional.of(response);
|
||||||
} catch (HttpConnectTimeoutException | ConnectException e) {
|
} catch (HttpConnectTimeoutException | ConnectException e) {
|
||||||
if (proxySettings.isAutoDiscard())
|
if (proxySettings.isAutoDiscard())
|
||||||
proxySettings.remove();
|
proxySettings.remove();
|
||||||
proxySettings.setLastSuccess(false);
|
|
||||||
throw new TikTokProxyRequestException(e);
|
throw new TikTokProxyRequestException(e);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported
|
if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported
|
||||||
@@ -121,14 +117,12 @@ public class HttpProxyClient extends HttpClient
|
|||||||
|
|
||||||
var response = createHttpResponse(body, toUrl(), responseInfo);
|
var response = createHttpResponse(body, toUrl(), responseInfo);
|
||||||
|
|
||||||
proxySettings.setLastSuccess(true);
|
|
||||||
return Optional.of(response);
|
return Optional.of(response);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported
|
if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported
|
||||||
return super.toResponse();
|
return super.toResponse();
|
||||||
if (proxySettings.isAutoDiscard())
|
if (proxySettings.isAutoDiscard())
|
||||||
proxySettings.remove();
|
proxySettings.remove();
|
||||||
proxySettings.setLastSuccess(false);
|
|
||||||
throw new TikTokProxyRequestException(e);
|
throw new TikTokProxyRequestException(e);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new TikTokLiveRequestException(e);
|
throw new TikTokLiveRequestException(e);
|
||||||
@@ -137,7 +131,7 @@ public class HttpProxyClient extends HttpClient
|
|||||||
throw new TikTokLiveRequestException("No more proxies available!");
|
throw new TikTokLiveRequestException("No more proxies available!");
|
||||||
} catch (NoSuchAlgorithmException | MalformedURLException | KeyManagementException e) {
|
} catch (NoSuchAlgorithmException | MalformedURLException | KeyManagementException e) {
|
||||||
// Should never be reached!
|
// Should never be reached!
|
||||||
System.out.println("handleSocksProxyRequest()! If you see this message, reach us on discord!");
|
System.out.println("handleSocksProxyRequest: If you see this, message us on discord!");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
} catch (TikTokLiveRequestException e) {
|
} catch (TikTokLiveRequestException e) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ package io.github.jwdeveloper.tiktok.http.mappers;
|
|||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
|
import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
import io.github.jwdeveloper.tiktok.data.models.Picture;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.users.User;
|
import io.github.jwdeveloper.tiktok.data.models.users.User;
|
||||||
import io.github.jwdeveloper.tiktok.data.models.users.UserAttribute;
|
import io.github.jwdeveloper.tiktok.data.models.users.UserAttribute;
|
||||||
@@ -64,6 +65,9 @@ public class LiveDataMapper {
|
|||||||
default -> LiveData.LiveStatus.HostNotFound;
|
default -> LiveData.LiveStatus.HostNotFound;
|
||||||
};
|
};
|
||||||
response.setLiveStatus(statusValue);
|
response.setLiveStatus(statusValue);
|
||||||
|
} else if (data.has("prompts") && jsonObject.has("status_code") &&
|
||||||
|
data.get("prompts").getAsString().isEmpty() && jsonObject.get("status_code").isJsonPrimitive()) {
|
||||||
|
response.setAgeRestricted(jsonObject.get("status_code").getAsInt() == TikTokLiveHttpClient.TIKTOK_AGE_RESTRICTED_CODE);
|
||||||
} else {
|
} else {
|
||||||
response.setLiveStatus(LiveData.LiveStatus.HostNotFound);
|
response.setLiveStatus(LiveData.LiveStatus.HostNotFound);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of this software and associated documentation files (the
|
|
||||||
* "Software"), to deal in the Software without restriction, including
|
|
||||||
* without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
* the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package io.github.jwdeveloper.tiktok.http.mappers;
|
|
||||||
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
import io.github.jwdeveloper.tiktok.data.requests.SignServerResponse;
|
|
||||||
|
|
||||||
public class SignServerResponseMapper {
|
|
||||||
public SignServerResponse map(String json) {
|
|
||||||
var parsedJson = JsonParser.parseString(json);
|
|
||||||
var jsonObject = parsedJson.getAsJsonObject();
|
|
||||||
|
|
||||||
var signUrl = jsonObject.get("signedUrl").getAsString();
|
|
||||||
var userAgent = jsonObject.get("User-Agent").getAsString();
|
|
||||||
return new SignServerResponse(signUrl, userAgent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -97,6 +97,10 @@ public class CodeExample {
|
|||||||
//RoomId can be used as an override if you're having issues with HostId.
|
//RoomId can be used as an override if you're having issues with HostId.
|
||||||
//You can find it in the HTML for the livestream-page
|
//You can find it in the HTML for the livestream-page
|
||||||
settings.setRoomId("XXXXXXXXXXXXXXXXX");
|
settings.setRoomId("XXXXXXXXXXXXXXXXX");
|
||||||
|
|
||||||
|
//Optional:
|
||||||
|
//API Key for increased limit to signing server
|
||||||
|
settings.setApiKey("XXXXXXXXXXXXXXXXX");
|
||||||
})
|
})
|
||||||
.buildAndConnect();
|
.buildAndConnect();
|
||||||
// </code>
|
// </code>
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
public class TikTokLiveRecorder
|
public class TikTokLiveRecorder
|
||||||
{
|
{
|
||||||
|
|
||||||
public static LiveRecorder use(Consumer<RecorderSettings> consumer)
|
public static LiveRecorder use(Consumer<RecorderSettings> consumer)
|
||||||
{
|
{
|
||||||
return new RecorderListener(consumer);
|
return new RecorderListener(consumer);
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ 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.http.TikTokRoomDataResponseEvent;
|
||||||
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings;
|
||||||
import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException;
|
|
||||||
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.*;
|
||||||
import io.github.jwdeveloper.tiktok.extension.recorder.impl.enums.LiveQuality;
|
import io.github.jwdeveloper.tiktok.extension.recorder.impl.enums.LiveQuality;
|
||||||
@@ -70,16 +69,12 @@ public class RecorderListener implements LiveRecorder {
|
|||||||
var json = event.getLiveData().getJson();
|
var json = event.getLiveData().getJson();
|
||||||
|
|
||||||
liveClient.getLogger().info("Searching for live download url");
|
liveClient.getLogger().info("Searching for live download url");
|
||||||
if (settings.getPrepareDownloadData() != null) {
|
downloadData = settings.getPrepareDownloadData() != null ? settings.getPrepareDownloadData().apply(json) : mapToDownloadData(json);
|
||||||
downloadData = settings.getPrepareDownloadData().apply(json);
|
|
||||||
} else {
|
|
||||||
downloadData = mapToDownloadData(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downloadData.getDownloadLiveUrl().isEmpty()) {
|
if (downloadData.getDownloadLiveUrl().isEmpty())
|
||||||
throw new TikTokLiveException("Unable to find download live url!");
|
liveClient.getLogger().warning("Unable to find download live url!");
|
||||||
}
|
else
|
||||||
liveClient.getLogger().info("Live download url found!");
|
liveClient.getLogger().info("Live download url found!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TikTokEventObserver
|
@TikTokEventObserver
|
||||||
@@ -88,13 +83,13 @@ public class RecorderListener implements LiveRecorder {
|
|||||||
try {
|
try {
|
||||||
var bufferSize = 1024;
|
var bufferSize = 1024;
|
||||||
var url = new URL(downloadData.getFullUrl());
|
var url = new URL(downloadData.getFullUrl());
|
||||||
HttpsURLConnection socksConnection = (HttpsURLConnection) url.openConnection();
|
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||||
var headers = LiveClientSettings.DefaultRequestHeaders();
|
var headers = LiveClientSettings.DefaultRequestHeaders();
|
||||||
for (var entry : headers.entrySet()) {
|
for (var entry : headers.entrySet()) {
|
||||||
socksConnection.setRequestProperty(entry.getKey(), entry.getValue());
|
connection.setRequestProperty(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
var in = new BufferedInputStream(socksConnection.getInputStream());
|
var in = new BufferedInputStream(connection.getInputStream());
|
||||||
var path = settings.getOutputPath() + File.separator + settings.getOutputFileName();
|
var path = settings.getOutputPath() + File.separator + settings.getOutputFileName();
|
||||||
var file = new File(path);
|
var file = new File(path);
|
||||||
file.getParentFile().mkdirs();
|
file.getParentFile().mkdirs();
|
||||||
|
|||||||
Reference in New Issue
Block a user