From 243ce9bc949be3f4df556c15b076f4dbfbce202c Mon Sep 17 00:00:00 2001 From: kohlerpop1 Date: Thu, 15 Feb 2024 11:46:13 -0500 Subject: [PATCH 1/4] Added PreConnectionEvent with LiveType, made optimizations, and added fallback to default request in proxy class in case proxy protocol is not supported by TikTok or Signing server. --- .../data/requests/SignServerResponse.java | 35 --------- .../data/settings/LiveClientSettings.java | 5 ++ .../data/settings/ProxyClientSettings.java | 9 +-- .../tiktok/http/LiveHttpClient.java | 29 ++++---- .../jwdeveloper/tiktok/TikTokLiveClient.java | 31 ++++---- .../tiktok/TikTokLiveClientBuilder.java | 3 +- .../tiktok/TikTokLiveHttpClient.java | 73 ++++--------------- .../tiktok/http/HttpProxyClient.java | 10 +-- .../tiktok/http/mappers/LiveDataMapper.java | 4 + .../mappers/SignServerResponseMapper.java | 37 ---------- .../jwdeveloper/tiktok/CodeExample.java | 6 +- .../recorder/TikTokLiveRecorder.java | 3 +- .../recorder/impl/RecorderListener.java | 21 ++---- 13 files changed, 78 insertions(+), 188 deletions(-) delete mode 100644 API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/SignServerResponse.java delete mode 100644 Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/SignServerResponseMapper.java diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/SignServerResponse.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/SignServerResponse.java deleted file mode 100644 index cedfd675..00000000 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/SignServerResponse.java +++ /dev/null @@ -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; -} diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java index 26d6f624..e8c6a157 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java @@ -80,6 +80,11 @@ public class LiveClientSettings { */ private String roomId; + /** + * Optional: API Key for increased limit to signing server + */ + private String apiKey; + public static LiveClientSettings createDefault() { var httpSettings = new HttpClientSettings(); diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/ProxyClientSettings.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/ProxyClientSettings.java index e6ca0f82..ab295e1e 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/ProxyClientSettings.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/ProxyClientSettings.java @@ -33,7 +33,7 @@ @Setter public class ProxyClientSettings implements Iterator { - private boolean enabled, lastSuccess, autoDiscard = true, fallback = true; + private boolean enabled, autoDiscard = true, fallback = true; private Rotation rotation = Rotation.CONSECUTIVE; private final List proxyList = new ArrayList<>(); private int index = -1; @@ -63,10 +63,6 @@ public boolean hasNext() { @Override public ProxyData next() { - return lastSuccess ? proxyList.get(index) : rotate(); - } - - public ProxyData rotate() { var nextProxy = switch (rotation) { case CONSECUTIVE -> { @@ -84,12 +80,11 @@ public ProxyData rotate() { }; onProxyUpdated.accept(nextProxy); return nextProxy; - } + } @Override public void remove() { proxyList.remove(index); - lastSuccess = false; // index is no longer valid and lastSuccess needs falsified } public void setIndex(int index) { diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java b/API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java index 78bb031d..7646bf7d 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java @@ -27,9 +27,8 @@ import io.github.jwdeveloper.tiktok.data.requests.LiveData; import io.github.jwdeveloper.tiktok.data.requests.LiveUserData; -public interface LiveHttpClient { - - +public interface LiveHttpClient +{ /** * @return list of gifts that are available in your country */ @@ -37,28 +36,32 @@ public interface LiveHttpClient { /** * Returns information about user that is having a livestream - * - * @param userName - * @return + * @param userName name of user + * @return {@link LiveUserData.Response} */ - LiveUserData.Response fetchLiveUserData(String userName); + default LiveUserData.Response fetchLiveUserData(String userName) { + return fetchLiveUserData(new LiveUserData.Request(userName)); + } LiveUserData.Response fetchLiveUserData(LiveUserData.Request request); /** * @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData - * @return + * @return {@link LiveData.Response} */ - LiveData.Response fetchLiveData(String roomId); + default LiveData.Response fetchLiveData(String roomId) { + return fetchLiveData(new LiveData.Request(roomId)); + } LiveData.Response fetchLiveData(LiveData.Request request); - /** * @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData - * @return + * @return {@link LiveConnectionData.Response} */ - LiveConnectionData.Response fetchLiveConnectionData(String roomId); + default LiveConnectionData.Response fetchLiveConnectionData(String roomId) { + return fetchLiveConnectionData(new LiveConnectionData.Request(roomId)); + } LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request); -} +} \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java index a7ec44c6..807fd79d 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java @@ -32,8 +32,7 @@ import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData; import io.github.jwdeveloper.tiktok.data.requests.LiveData; import io.github.jwdeveloper.tiktok.data.requests.LiveUserData; -import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; -import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException; +import io.github.jwdeveloper.tiktok.exceptions.*; import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; import io.github.jwdeveloper.tiktok.listener.ListenersManager; import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager; @@ -127,22 +126,26 @@ public void tryConnect() { var userData = httpClient.fetchLiveUserData(userDataRequest); liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp()); liveRoomInfo.setRoomId(userData.getRoomId()); - if (userData.getUserStatus() == LiveUserData.UserStatus.Offline) { - throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostUser()); - } - if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound) { - throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostUser()); - } + + if (userData.getUserStatus() == LiveUserData.UserStatus.Offline) + throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostName()); + + if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound) + throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostName()); var liveDataRequest = new LiveData.Request(userData.getRoomId()); var liveData = httpClient.fetchLiveData(liveDataRequest); + + if (liveData.isAgeRestricted()) + throw new TikTokLiveException("Livestream for "+liveRoomInfo.getHostName()+" is 18+ or age restricted!"); + + if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) + throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" could not be found."); + + if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline) + throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" not found, is the Host offline?"); + tikTokEventHandler.publish(this, new TikTokRoomDataResponseEvent(liveData)); - if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) { - throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found."); - } - if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline) { - throw new TikTokLiveOfflineHostException("LiveStream for not be found, is the Host offline?"); - } liveRoomInfo.setTitle(liveData.getTitle()); liveRoomInfo.setViewersCount(liveData.getViewers()); diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java index c8e207be..26a69e2a 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java @@ -99,7 +99,8 @@ public TikTokLiveClientBuilder configure(Consumer onConfigur } public TikTokLiveClientBuilder addListener(TikTokEventListener listener) { - listeners.add(listener); + if (listener != null) + listeners.add(listener); return this; } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java index f3453832..8e2b5011 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java @@ -36,10 +36,9 @@ public class TikTokLiveHttpClient implements LiveHttpClient { /** - * Signing API by Isaac Kogan - * https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures - */ - private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/sign_url"; + * Signing API by Isaac Kogan + */ + private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch"; private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/"; private static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/"; @@ -47,7 +46,6 @@ public class TikTokLiveHttpClient implements LiveHttpClient { private final LiveClientSettings clientSettings; private final LiveUserDataMapper liveUserDataMapper; private final LiveDataMapper liveDataMapper; - private final SignServerResponseMapper signServerResponseMapper; private final GiftsDataMapper giftsDataMapper; public TikTokLiveHttpClient(HttpClientFactory factory, LiveClientSettings settings) { @@ -55,7 +53,6 @@ public TikTokLiveHttpClient(HttpClientFactory factory, LiveClientSettings settin clientSettings = settings; liveUserDataMapper = new LiveUserDataMapper(); liveDataMapper = new LiveDataMapper(); - signServerResponseMapper = new SignServerResponseMapper(); giftsDataMapper = new GiftsDataMapper(); } @@ -94,12 +91,6 @@ public GiftsData.Response fetchGiftsData() { return giftsDataMapper.map(json); } - - @Override - public LiveUserData.Response fetchLiveUserData(String userName) { - return fetchLiveUserData(new LiveUserData.Request(userName)); - } - @Override public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) { var url = TIKTOK_URL_WEB + "api-live/user/room"; @@ -136,11 +127,6 @@ public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) { return liveUserDataMapper.map(json); } - @Override - public LiveData.Response fetchLiveData(String roomId) { - return fetchLiveData(new LiveData.Request(roomId)); - } - @Override public LiveData.Response fetchLiveData(LiveData.Request request) { var url = TIKTOK_URL_WEBCAST + "room/info"; @@ -175,20 +161,13 @@ public LiveData.Response fetchLiveData(LiveData.Request request) { return liveDataMapper.map(json); } - @Override - public LiveConnectionData.Response fetchLiveConnectionData(String roomId) { - return fetchLiveConnectionData(new LiveConnectionData.Request(roomId)); - } - @Override public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) { - HttpResponse credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> { - SignServerResponse signServerResponse = getSignedUrl(request.getRoomId()); - return getWebsocketCredentialsResponse(signServerResponse.getSignedUrl()); - }); + HttpResponse credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> getStarterPayload(request.getRoomId())); try { - var optionalHeader = credentialsResponse.headers().firstValue("set-cookie"); + System.out.println(credentialsResponse.headers().map()); + var optionalHeader = credentialsResponse.headers().firstValue("x-set-tt-cookie"); if (optionalHeader.isEmpty()) { throw new TikTokSignServerException("Sign server did not return the set-cookie header"); } @@ -210,39 +189,21 @@ public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Re } } - SignServerResponse getSignedUrl(String roomId) { - var urlToSign = httpFactory - .client(TikTokLiveHttpClient.TIKTOK_URL_WEBCAST + "im/fetch") - .withParam("room_id", roomId) - .build() - .toUrl(); + HttpResponse getStarterPayload(String room_id) { + HttpClientBuilder builder = httpFactory.client(TIKTOK_SIGN_API) + .withParam("client", "ttlive-java") + .withParam("uuc", "1") + .withParam("room_id", room_id); + if (clientSettings.getApiKey() != null) + builder.withParam("apiKey", clientSettings.getApiKey()); - var optional = httpFactory - .client(TikTokLiveHttpClient.TIKTOK_SIGN_API) - .withParam("client", "ttlive-java") - .withParam("uuc", "1") - .withParam("url", urlToSign.toString()) - .build() - .toJsonResponse(); + var optional = builder.build().toResponse(); if (optional.isEmpty()) { - throw new TikTokSignServerException("Unable to sign url: " + urlToSign); - } - - var json = optional.get(); - return signServerResponseMapper.map(json); - } - - HttpResponse getWebsocketCredentialsResponse(String signedUrl) { - var optionalResponse = httpFactory - .clientEmpty(signedUrl) - .build() - .toResponse(); - if (optionalResponse.isEmpty()) { throw new TikTokSignServerException("Unable to get websocket connection credentials"); } - return optionalResponse.get(); + return optional.get(); } Optional> getOptionalProxyResponse(LiveConnectionData.Request request) { @@ -250,9 +211,7 @@ Optional> getOptionalProxyResponse(LiveConnectionData.Reque if (proxyClientSettings.isEnabled()) { while (proxyClientSettings.hasNext()) { try { - SignServerResponse signServerResponse = getSignedUrl(request.getRoomId()); - HttpResponse credentialsResponse = getWebsocketCredentialsResponse(signServerResponse.getSignedUrl()); - clientSettings.getHttpSettings().getProxyClientSettings().rotate(); + HttpResponse credentialsResponse = getStarterPayload(request.getRoomId()); return Optional.of(credentialsResponse); } catch (TikTokProxyRequestException | TikTokSignServerException ignored) {} } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java index 580cfd6f..6e3b75a8 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java @@ -67,16 +67,12 @@ public Optional> handleHttpProxyRequest() { var request = prepareGetRequest(); var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); - if (response.statusCode() != 200) { - proxySettings.setLastSuccess(false); + if (response.statusCode() != 200) continue; - } - proxySettings.setLastSuccess(true); return Optional.of(response); } catch (HttpConnectTimeoutException | ConnectException e) { if (proxySettings.isAutoDiscard()) proxySettings.remove(); - proxySettings.setLastSuccess(false); throw new TikTokProxyRequestException(e); } catch (IOException e) { if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported @@ -121,14 +117,12 @@ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {} var response = createHttpResponse(body, toUrl(), responseInfo); - proxySettings.setLastSuccess(true); return Optional.of(response); } catch (IOException e) { if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported return super.toResponse(); if (proxySettings.isAutoDiscard()) proxySettings.remove(); - proxySettings.setLastSuccess(false); throw new TikTokProxyRequestException(e); } catch (Exception e) { throw new TikTokLiveRequestException(e); @@ -137,7 +131,7 @@ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {} throw new TikTokLiveRequestException("No more proxies available!"); } catch (NoSuchAlgorithmException | MalformedURLException | KeyManagementException e) { // Should never be reached! - System.out.println("handleSocksProxyRequest()! If you see this message, reach us on discord!"); + System.out.println("handleSocksProxyRequest: If you see this, message us on discord!"); e.printStackTrace(); return Optional.empty(); } catch (TikTokLiveRequestException e) { diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java index 966ff2f0..b7486187 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java @@ -64,6 +64,10 @@ public LiveData.Response map(String json) { default -> LiveData.LiveStatus.HostNotFound; }; response.setLiveStatus(statusValue); + } else if (data.has("prompts") && jsonObject.has("status_code") && + data.get("prompts").getAsString().isEmpty() && jsonObject.get("status_code").isJsonPrimitive()) { + // 4003110 is age restriction code + response.setAgeRestricted(jsonObject.get("status_code").getAsInt() == 4003110); } else { response.setLiveStatus(LiveData.LiveStatus.HostNotFound); } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/SignServerResponseMapper.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/SignServerResponseMapper.java deleted file mode 100644 index d95a6ae4..00000000 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/SignServerResponseMapper.java +++ /dev/null @@ -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); - } -} diff --git a/Tools-ReadmeGenerator/src/main/java/io/github/jwdeveloper/tiktok/CodeExample.java b/Tools-ReadmeGenerator/src/main/java/io/github/jwdeveloper/tiktok/CodeExample.java index 6951b47e..f81db55e 100644 --- a/Tools-ReadmeGenerator/src/main/java/io/github/jwdeveloper/tiktok/CodeExample.java +++ b/Tools-ReadmeGenerator/src/main/java/io/github/jwdeveloper/tiktok/CodeExample.java @@ -97,10 +97,14 @@ public static void configExample() { //RoomId can be used as an override if you're having issues with HostId. //You can find it in the HTML for the livestream-page settings.setRoomId("XXXXXXXXXXXXXXXXX"); + + //Optional: + //API Key for increased limit to signing server + settings.setApiKey("XXXXXXXXXXXXXXXXX"); }) .buildAndConnect(); // } -} +} \ No newline at end of file diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/TikTokLiveRecorder.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/TikTokLiveRecorder.java index ca0ad623..4db5f381 100644 --- a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/TikTokLiveRecorder.java +++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/TikTokLiveRecorder.java @@ -30,7 +30,6 @@ public class TikTokLiveRecorder { - public static LiveRecorder use(Consumer consumer) { return new RecorderListener(consumer); @@ -40,4 +39,4 @@ public static LiveRecorder use() { return use(x ->{}); } -} +} \ No newline at end of file diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java index ed28478b..1f7da1d7 100644 --- a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java +++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java @@ -27,7 +27,6 @@ import io.github.jwdeveloper.tiktok.data.events.*; import io.github.jwdeveloper.tiktok.data.events.http.TikTokRoomDataResponseEvent; import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; -import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; 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.enums.LiveQuality; @@ -70,16 +69,12 @@ private void onResponse(LiveClient liveClient, TikTokRoomDataResponseEvent event var json = event.getLiveData().getJson(); liveClient.getLogger().info("Searching for live download url"); - if (settings.getPrepareDownloadData() != null) { - downloadData = settings.getPrepareDownloadData().apply(json); - } else { - downloadData = mapToDownloadData(json); - } + downloadData = settings.getPrepareDownloadData() != null ? settings.getPrepareDownloadData().apply(json) : mapToDownloadData(json); - if (downloadData.getDownloadLiveUrl().isEmpty()) { - throw new TikTokLiveException("Unable to find download live url!"); - } - liveClient.getLogger().info("Live download url found!"); + if (downloadData.getDownloadLiveUrl().isEmpty()) + liveClient.getLogger().warning("Unable to find download live url!"); + else + liveClient.getLogger().info("Live download url found!"); } @TikTokEventObserver @@ -88,13 +83,13 @@ private void onConnected(LiveClient liveClient, TikTokConnectedEvent event) { try { var bufferSize = 1024; var url = new URL(downloadData.getFullUrl()); - HttpsURLConnection socksConnection = (HttpsURLConnection) url.openConnection(); + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); var headers = LiveClientSettings.DefaultRequestHeaders(); for (var entry : headers.entrySet()) { - socksConnection.setRequestProperty(entry.getKey(), entry.getValue()); + connection.setRequestProperty(entry.getKey(), entry.getValue()); } - var in = new BufferedInputStream(socksConnection.getInputStream()); + var in = new BufferedInputStream(connection.getInputStream()); var path = settings.getOutputPath() + File.separator + settings.getOutputFileName(); var file = new File(path); file.getParentFile().mkdirs(); From c1105f13242b97c3744ae0be3d95357a3d674f64 Mon Sep 17 00:00:00 2001 From: kohlerpop1 Date: Thu, 15 Feb 2024 11:46:13 -0500 Subject: [PATCH 2/4] Switched to new Signing Server endpoint and more --- .../data/requests/SignServerResponse.java | 35 --------- .../data/settings/LiveClientSettings.java | 5 ++ .../data/settings/ProxyClientSettings.java | 9 +-- .../tiktok/http/LiveHttpClient.java | 29 ++++---- .../jwdeveloper/tiktok/TikTokLiveClient.java | 31 ++++---- .../tiktok/TikTokLiveClientBuilder.java | 3 +- .../tiktok/TikTokLiveHttpClient.java | 73 ++++--------------- .../tiktok/http/HttpProxyClient.java | 10 +-- .../tiktok/http/mappers/LiveDataMapper.java | 4 + .../mappers/SignServerResponseMapper.java | 37 ---------- .../jwdeveloper/tiktok/CodeExample.java | 6 +- .../recorder/TikTokLiveRecorder.java | 3 +- .../recorder/impl/RecorderListener.java | 21 ++---- 13 files changed, 78 insertions(+), 188 deletions(-) delete mode 100644 API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/SignServerResponse.java delete mode 100644 Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/SignServerResponseMapper.java diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/SignServerResponse.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/SignServerResponse.java deleted file mode 100644 index cedfd675..00000000 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/requests/SignServerResponse.java +++ /dev/null @@ -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; -} diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java index 26d6f624..e8c6a157 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/LiveClientSettings.java @@ -80,6 +80,11 @@ public class LiveClientSettings { */ private String roomId; + /** + * Optional: API Key for increased limit to signing server + */ + private String apiKey; + public static LiveClientSettings createDefault() { var httpSettings = new HttpClientSettings(); diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/ProxyClientSettings.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/ProxyClientSettings.java index e6ca0f82..ab295e1e 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/ProxyClientSettings.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/settings/ProxyClientSettings.java @@ -33,7 +33,7 @@ @Setter public class ProxyClientSettings implements Iterator { - private boolean enabled, lastSuccess, autoDiscard = true, fallback = true; + private boolean enabled, autoDiscard = true, fallback = true; private Rotation rotation = Rotation.CONSECUTIVE; private final List proxyList = new ArrayList<>(); private int index = -1; @@ -63,10 +63,6 @@ public boolean hasNext() { @Override public ProxyData next() { - return lastSuccess ? proxyList.get(index) : rotate(); - } - - public ProxyData rotate() { var nextProxy = switch (rotation) { case CONSECUTIVE -> { @@ -84,12 +80,11 @@ public ProxyData rotate() { }; onProxyUpdated.accept(nextProxy); return nextProxy; - } + } @Override public void remove() { proxyList.remove(index); - lastSuccess = false; // index is no longer valid and lastSuccess needs falsified } public void setIndex(int index) { diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java b/API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java index 78bb031d..7646bf7d 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/http/LiveHttpClient.java @@ -27,9 +27,8 @@ import io.github.jwdeveloper.tiktok.data.requests.LiveData; import io.github.jwdeveloper.tiktok.data.requests.LiveUserData; -public interface LiveHttpClient { - - +public interface LiveHttpClient +{ /** * @return list of gifts that are available in your country */ @@ -37,28 +36,32 @@ public interface LiveHttpClient { /** * Returns information about user that is having a livestream - * - * @param userName - * @return + * @param userName name of user + * @return {@link LiveUserData.Response} */ - LiveUserData.Response fetchLiveUserData(String userName); + default LiveUserData.Response fetchLiveUserData(String userName) { + return fetchLiveUserData(new LiveUserData.Request(userName)); + } LiveUserData.Response fetchLiveUserData(LiveUserData.Request request); /** * @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData - * @return + * @return {@link LiveData.Response} */ - LiveData.Response fetchLiveData(String roomId); + default LiveData.Response fetchLiveData(String roomId) { + return fetchLiveData(new LiveData.Request(roomId)); + } LiveData.Response fetchLiveData(LiveData.Request request); - /** * @param roomId can be obtained from browsers cookies or by invoked fetchLiveUserData - * @return + * @return {@link LiveConnectionData.Response} */ - LiveConnectionData.Response fetchLiveConnectionData(String roomId); + default LiveConnectionData.Response fetchLiveConnectionData(String roomId) { + return fetchLiveConnectionData(new LiveConnectionData.Request(roomId)); + } LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request); -} +} \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java index a7ec44c6..807fd79d 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java @@ -32,8 +32,7 @@ import io.github.jwdeveloper.tiktok.data.requests.LiveConnectionData; import io.github.jwdeveloper.tiktok.data.requests.LiveData; import io.github.jwdeveloper.tiktok.data.requests.LiveUserData; -import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; -import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveOfflineHostException; +import io.github.jwdeveloper.tiktok.exceptions.*; import io.github.jwdeveloper.tiktok.gifts.TikTokGiftManager; import io.github.jwdeveloper.tiktok.listener.ListenersManager; import io.github.jwdeveloper.tiktok.listener.TikTokListenersManager; @@ -127,22 +126,26 @@ public void tryConnect() { var userData = httpClient.fetchLiveUserData(userDataRequest); liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp()); liveRoomInfo.setRoomId(userData.getRoomId()); - if (userData.getUserStatus() == LiveUserData.UserStatus.Offline) { - throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostUser()); - } - if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound) { - throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostUser()); - } + + if (userData.getUserStatus() == LiveUserData.UserStatus.Offline) + throw new TikTokLiveOfflineHostException("User is offline: "+liveRoomInfo.getHostName()); + + if (userData.getUserStatus() == LiveUserData.UserStatus.NotFound) + throw new TikTokLiveOfflineHostException("User not found: "+liveRoomInfo.getHostName()); var liveDataRequest = new LiveData.Request(userData.getRoomId()); var liveData = httpClient.fetchLiveData(liveDataRequest); + + if (liveData.isAgeRestricted()) + throw new TikTokLiveException("Livestream for "+liveRoomInfo.getHostName()+" is 18+ or age restricted!"); + + if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) + throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" could not be found."); + + if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline) + throw new TikTokLiveOfflineHostException("LiveStream for "+liveRoomInfo.getHostName()+" not found, is the Host offline?"); + tikTokEventHandler.publish(this, new TikTokRoomDataResponseEvent(liveData)); - if (liveData.getLiveStatus() == LiveData.LiveStatus.HostNotFound) { - throw new TikTokLiveOfflineHostException("LiveStream for Host name could not be found."); - } - if (liveData.getLiveStatus() == LiveData.LiveStatus.HostOffline) { - throw new TikTokLiveOfflineHostException("LiveStream for not be found, is the Host offline?"); - } liveRoomInfo.setTitle(liveData.getTitle()); liveRoomInfo.setViewersCount(liveData.getViewers()); diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java index c8e207be..26a69e2a 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java @@ -99,7 +99,8 @@ public TikTokLiveClientBuilder configure(Consumer onConfigur } public TikTokLiveClientBuilder addListener(TikTokEventListener listener) { - listeners.add(listener); + if (listener != null) + listeners.add(listener); return this; } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java index f3453832..8e2b5011 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java @@ -36,10 +36,9 @@ public class TikTokLiveHttpClient implements LiveHttpClient { /** - * Signing API by Isaac Kogan - * https://github-wiki-see.page/m/isaackogan/TikTokLive/wiki/All-About-Signatures - */ - private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/sign_url"; + * Signing API by Isaac Kogan + */ + private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch"; private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/"; private static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/"; @@ -47,7 +46,6 @@ public class TikTokLiveHttpClient implements LiveHttpClient { private final LiveClientSettings clientSettings; private final LiveUserDataMapper liveUserDataMapper; private final LiveDataMapper liveDataMapper; - private final SignServerResponseMapper signServerResponseMapper; private final GiftsDataMapper giftsDataMapper; public TikTokLiveHttpClient(HttpClientFactory factory, LiveClientSettings settings) { @@ -55,7 +53,6 @@ public TikTokLiveHttpClient(HttpClientFactory factory, LiveClientSettings settin clientSettings = settings; liveUserDataMapper = new LiveUserDataMapper(); liveDataMapper = new LiveDataMapper(); - signServerResponseMapper = new SignServerResponseMapper(); giftsDataMapper = new GiftsDataMapper(); } @@ -94,12 +91,6 @@ public GiftsData.Response fetchGiftsData() { return giftsDataMapper.map(json); } - - @Override - public LiveUserData.Response fetchLiveUserData(String userName) { - return fetchLiveUserData(new LiveUserData.Request(userName)); - } - @Override public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) { var url = TIKTOK_URL_WEB + "api-live/user/room"; @@ -136,11 +127,6 @@ public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) { return liveUserDataMapper.map(json); } - @Override - public LiveData.Response fetchLiveData(String roomId) { - return fetchLiveData(new LiveData.Request(roomId)); - } - @Override public LiveData.Response fetchLiveData(LiveData.Request request) { var url = TIKTOK_URL_WEBCAST + "room/info"; @@ -175,20 +161,13 @@ public LiveData.Response fetchLiveData(LiveData.Request request) { return liveDataMapper.map(json); } - @Override - public LiveConnectionData.Response fetchLiveConnectionData(String roomId) { - return fetchLiveConnectionData(new LiveConnectionData.Request(roomId)); - } - @Override public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Request request) { - HttpResponse credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> { - SignServerResponse signServerResponse = getSignedUrl(request.getRoomId()); - return getWebsocketCredentialsResponse(signServerResponse.getSignedUrl()); - }); + HttpResponse credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> getStarterPayload(request.getRoomId())); try { - var optionalHeader = credentialsResponse.headers().firstValue("set-cookie"); + System.out.println(credentialsResponse.headers().map()); + var optionalHeader = credentialsResponse.headers().firstValue("x-set-tt-cookie"); if (optionalHeader.isEmpty()) { throw new TikTokSignServerException("Sign server did not return the set-cookie header"); } @@ -210,39 +189,21 @@ public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Re } } - SignServerResponse getSignedUrl(String roomId) { - var urlToSign = httpFactory - .client(TikTokLiveHttpClient.TIKTOK_URL_WEBCAST + "im/fetch") - .withParam("room_id", roomId) - .build() - .toUrl(); + HttpResponse getStarterPayload(String room_id) { + HttpClientBuilder builder = httpFactory.client(TIKTOK_SIGN_API) + .withParam("client", "ttlive-java") + .withParam("uuc", "1") + .withParam("room_id", room_id); + if (clientSettings.getApiKey() != null) + builder.withParam("apiKey", clientSettings.getApiKey()); - var optional = httpFactory - .client(TikTokLiveHttpClient.TIKTOK_SIGN_API) - .withParam("client", "ttlive-java") - .withParam("uuc", "1") - .withParam("url", urlToSign.toString()) - .build() - .toJsonResponse(); + var optional = builder.build().toResponse(); if (optional.isEmpty()) { - throw new TikTokSignServerException("Unable to sign url: " + urlToSign); - } - - var json = optional.get(); - return signServerResponseMapper.map(json); - } - - HttpResponse getWebsocketCredentialsResponse(String signedUrl) { - var optionalResponse = httpFactory - .clientEmpty(signedUrl) - .build() - .toResponse(); - if (optionalResponse.isEmpty()) { throw new TikTokSignServerException("Unable to get websocket connection credentials"); } - return optionalResponse.get(); + return optional.get(); } Optional> getOptionalProxyResponse(LiveConnectionData.Request request) { @@ -250,9 +211,7 @@ Optional> getOptionalProxyResponse(LiveConnectionData.Reque if (proxyClientSettings.isEnabled()) { while (proxyClientSettings.hasNext()) { try { - SignServerResponse signServerResponse = getSignedUrl(request.getRoomId()); - HttpResponse credentialsResponse = getWebsocketCredentialsResponse(signServerResponse.getSignedUrl()); - clientSettings.getHttpSettings().getProxyClientSettings().rotate(); + HttpResponse credentialsResponse = getStarterPayload(request.getRoomId()); return Optional.of(credentialsResponse); } catch (TikTokProxyRequestException | TikTokSignServerException ignored) {} } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java index 580cfd6f..6e3b75a8 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/HttpProxyClient.java @@ -67,16 +67,12 @@ public Optional> handleHttpProxyRequest() { var request = prepareGetRequest(); var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); - if (response.statusCode() != 200) { - proxySettings.setLastSuccess(false); + if (response.statusCode() != 200) continue; - } - proxySettings.setLastSuccess(true); return Optional.of(response); } catch (HttpConnectTimeoutException | ConnectException e) { if (proxySettings.isAutoDiscard()) proxySettings.remove(); - proxySettings.setLastSuccess(false); throw new TikTokProxyRequestException(e); } catch (IOException e) { if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported @@ -121,14 +117,12 @@ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {} var response = createHttpResponse(body, toUrl(), responseInfo); - proxySettings.setLastSuccess(true); return Optional.of(response); } catch (IOException e) { if (e.getMessage().contains("503") && proxySettings.isFallback()) // Indicates proxy protocol is not supported return super.toResponse(); if (proxySettings.isAutoDiscard()) proxySettings.remove(); - proxySettings.setLastSuccess(false); throw new TikTokProxyRequestException(e); } catch (Exception e) { throw new TikTokLiveRequestException(e); @@ -137,7 +131,7 @@ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {} throw new TikTokLiveRequestException("No more proxies available!"); } catch (NoSuchAlgorithmException | MalformedURLException | KeyManagementException e) { // Should never be reached! - System.out.println("handleSocksProxyRequest()! If you see this message, reach us on discord!"); + System.out.println("handleSocksProxyRequest: If you see this, message us on discord!"); e.printStackTrace(); return Optional.empty(); } catch (TikTokLiveRequestException e) { diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java index 966ff2f0..b7486187 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java @@ -64,6 +64,10 @@ public LiveData.Response map(String json) { default -> LiveData.LiveStatus.HostNotFound; }; response.setLiveStatus(statusValue); + } else if (data.has("prompts") && jsonObject.has("status_code") && + data.get("prompts").getAsString().isEmpty() && jsonObject.get("status_code").isJsonPrimitive()) { + // 4003110 is age restriction code + response.setAgeRestricted(jsonObject.get("status_code").getAsInt() == 4003110); } else { response.setLiveStatus(LiveData.LiveStatus.HostNotFound); } diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/SignServerResponseMapper.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/SignServerResponseMapper.java deleted file mode 100644 index d95a6ae4..00000000 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/SignServerResponseMapper.java +++ /dev/null @@ -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); - } -} diff --git a/Tools-ReadmeGenerator/src/main/java/io/github/jwdeveloper/tiktok/CodeExample.java b/Tools-ReadmeGenerator/src/main/java/io/github/jwdeveloper/tiktok/CodeExample.java index 6951b47e..f81db55e 100644 --- a/Tools-ReadmeGenerator/src/main/java/io/github/jwdeveloper/tiktok/CodeExample.java +++ b/Tools-ReadmeGenerator/src/main/java/io/github/jwdeveloper/tiktok/CodeExample.java @@ -97,10 +97,14 @@ public static void configExample() { //RoomId can be used as an override if you're having issues with HostId. //You can find it in the HTML for the livestream-page settings.setRoomId("XXXXXXXXXXXXXXXXX"); + + //Optional: + //API Key for increased limit to signing server + settings.setApiKey("XXXXXXXXXXXXXXXXX"); }) .buildAndConnect(); // } -} +} \ No newline at end of file diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/TikTokLiveRecorder.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/TikTokLiveRecorder.java index ca0ad623..4db5f381 100644 --- a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/TikTokLiveRecorder.java +++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/TikTokLiveRecorder.java @@ -30,7 +30,6 @@ public class TikTokLiveRecorder { - public static LiveRecorder use(Consumer consumer) { return new RecorderListener(consumer); @@ -40,4 +39,4 @@ public static LiveRecorder use() { return use(x ->{}); } -} +} \ No newline at end of file diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java index ed28478b..1f7da1d7 100644 --- a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java +++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/RecorderListener.java @@ -27,7 +27,6 @@ import io.github.jwdeveloper.tiktok.data.events.*; import io.github.jwdeveloper.tiktok.data.events.http.TikTokRoomDataResponseEvent; import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; -import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; 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.enums.LiveQuality; @@ -70,16 +69,12 @@ private void onResponse(LiveClient liveClient, TikTokRoomDataResponseEvent event var json = event.getLiveData().getJson(); liveClient.getLogger().info("Searching for live download url"); - if (settings.getPrepareDownloadData() != null) { - downloadData = settings.getPrepareDownloadData().apply(json); - } else { - downloadData = mapToDownloadData(json); - } + downloadData = settings.getPrepareDownloadData() != null ? settings.getPrepareDownloadData().apply(json) : mapToDownloadData(json); - if (downloadData.getDownloadLiveUrl().isEmpty()) { - throw new TikTokLiveException("Unable to find download live url!"); - } - liveClient.getLogger().info("Live download url found!"); + if (downloadData.getDownloadLiveUrl().isEmpty()) + liveClient.getLogger().warning("Unable to find download live url!"); + else + liveClient.getLogger().info("Live download url found!"); } @TikTokEventObserver @@ -88,13 +83,13 @@ private void onConnected(LiveClient liveClient, TikTokConnectedEvent event) { try { var bufferSize = 1024; var url = new URL(downloadData.getFullUrl()); - HttpsURLConnection socksConnection = (HttpsURLConnection) url.openConnection(); + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); var headers = LiveClientSettings.DefaultRequestHeaders(); for (var entry : headers.entrySet()) { - socksConnection.setRequestProperty(entry.getKey(), entry.getValue()); + connection.setRequestProperty(entry.getKey(), entry.getValue()); } - var in = new BufferedInputStream(socksConnection.getInputStream()); + var in = new BufferedInputStream(connection.getInputStream()); var path = settings.getOutputPath() + File.separator + settings.getOutputFileName(); var file = new File(path); file.getParentFile().mkdirs(); From c89bcad894ae3aa1b25df2c1f28a6873de1eb219 Mon Sep 17 00:00:00 2001 From: kohlerpop1 Date: Thu, 15 Feb 2024 12:13:52 -0500 Subject: [PATCH 3/4] Removed System.out.println of response headers! --- .../java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java index 8e2b5011..6282cc05 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java @@ -166,7 +166,6 @@ public LiveConnectionData.Response fetchLiveConnectionData(LiveConnectionData.Re HttpResponse credentialsResponse = getOptionalProxyResponse(request).orElseGet(()-> getStarterPayload(request.getRoomId())); try { - System.out.println(credentialsResponse.headers().map()); var optionalHeader = credentialsResponse.headers().firstValue("x-set-tt-cookie"); if (optionalHeader.isEmpty()) { throw new TikTokSignServerException("Sign server did not return the set-cookie header"); From d471e87dd7c6d10be10b3bee8cdd77e16888aa9e Mon Sep 17 00:00:00 2001 From: kohlerpop1 Date: Thu, 15 Feb 2024 12:55:45 -0500 Subject: [PATCH 4/4] Converted magic number to constant AGE_RESTRICTED_CODE --- .../io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java | 1 + .../jwdeveloper/tiktok/http/mappers/LiveDataMapper.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java index 6282cc05..f1b95b69 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java @@ -41,6 +41,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient { private static final String TIKTOK_SIGN_API = "https://tiktok.eulerstream.com/webcast/fetch"; private static final String TIKTOK_URL_WEB = "https://www.tiktok.com/"; private static final String TIKTOK_URL_WEBCAST = "https://webcast.tiktok.com/webcast/"; + public static final int TIKTOK_AGE_RESTRICTED_CODE = 4003110; private final HttpClientFactory httpFactory; private final LiveClientSettings clientSettings; diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java index b7486187..4eb05ace 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/LiveDataMapper.java @@ -24,6 +24,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import io.github.jwdeveloper.tiktok.TikTokLiveHttpClient; import io.github.jwdeveloper.tiktok.data.models.Picture; import io.github.jwdeveloper.tiktok.data.models.users.User; import io.github.jwdeveloper.tiktok.data.models.users.UserAttribute; @@ -66,8 +67,7 @@ public LiveData.Response map(String json) { response.setLiveStatus(statusValue); } else if (data.has("prompts") && jsonObject.has("status_code") && data.get("prompts").getAsString().isEmpty() && jsonObject.get("status_code").isJsonPrimitive()) { - // 4003110 is age restriction code - response.setAgeRestricted(jsonObject.get("status_code").getAsInt() == 4003110); + response.setAgeRestricted(jsonObject.get("status_code").getAsInt() == TikTokLiveHttpClient.TIKTOK_AGE_RESTRICTED_CODE); } else { response.setLiveStatus(LiveData.LiveStatus.HostNotFound); }