diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/Picture.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/Picture.java index c55a053..45d2227 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/Picture.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/Picture.java @@ -93,7 +93,7 @@ public class Picture { } } - public static Picture Empty() { + public static Picture empty() { return new Picture(""); } diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/users/User.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/users/User.java index cab957b..06fa68b 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/users/User.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/users/User.java @@ -169,7 +169,7 @@ public class User { public static User EMPTY = new User(0L, "", - Picture.Empty(), + Picture.empty(), 0, 0, List.of(Badge.empty())); 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 03a3a69..3ebc928 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 @@ -33,20 +33,15 @@ import java.util.logging.Level; @Data public class LiveClientSettings { - /** - * TODO: give better description - *

- * sets client in the offline mode, so it do not connects to TikTok servers - * it makes sense to use it when you are testing client with your custom events + * Sets client to offline mode, prohibits connection to TikTok servers + * @apiNote Useful when testing client with custom events */ private boolean offline; /** - * TODO: give better description - *

- * Determines if gifts data is downloaded before TikTokLive starts, - * when `false` then client.giftManager() does not contain initial gifts + * Fetch and download gifts data before TikTokLive starts + * @apiNote If `false`, client.giftManager() does not contain initial gifts */ private boolean fetchGifts = true; 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 2fcdd09..8506db1 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 @@ -30,10 +30,14 @@ import io.github.jwdeveloper.tiktok.data.requests.LiveUserData; public interface LiveHttpClient { /** - * @return list of gifts that are available in your country + * @return {@link GiftsData.Response} list of gifts that are compiled and available on github */ GiftsData.Response fetchGiftsData(); + /** + * @return {@link GiftsData.Response} list of gifts that are available in your region / livestream + */ + GiftsData.Response fetchRoomGiftsData(String room_id); /** * Returns information about user that is having a livestream 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 cfbb449..15e98be 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java @@ -128,6 +128,9 @@ public class TikTokLiveClient implements LiveClient { liveRoomInfo.setStartTime(userData.getStartedAtTimeStamp()); liveRoomInfo.setRoomId(userData.getRoomId()); + if (clientSettings.isFetchGifts()) + giftsManager.attachGiftsList(httpClient.fetchRoomGiftsData(userData.getRoomId()).getGifts()); + if (userData.getUserStatus() == LiveUserData.UserStatus.Offline) throw new TikTokLiveOfflineHostException("User is offline: " + liveRoomInfo.getHostName()); 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 0d1098d..f180930 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java @@ -43,6 +43,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient 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 String TIKTOK_GIFTS_URL = "https://raw.githubusercontent.com/TikTok-LIVE-Private/GiftsGenerator/master/page/public/gifts.json"; + public static final String TIKTOK_ROOM_GIFTS_URL = TIKTOK_URL_WEBCAST+"gift/list/"; public static final int TIKTOK_AGE_RESTRICTED_CODE = 4003110; private final HttpClientFactory httpFactory; @@ -65,6 +66,31 @@ public class TikTokLiveHttpClient implements LiveHttpClient this(new HttpClientFactory(LiveClientSettings.createDefault()), LiveClientSettings.createDefault()); } + public GiftsData.Response fetchRoomGiftsData(String room_id) { + var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings(); + if (proxyClientSettings.isEnabled()) { + while (proxyClientSettings.hasNext()) { + try { + return getRoomGiftsData(room_id); + } catch (TikTokProxyRequestException ignored) {} + } + } + return getRoomGiftsData(room_id); + } + + public GiftsData.Response getRoomGiftsData(String room_id) { + var result = httpFactory.client(TIKTOK_ROOM_GIFTS_URL) + .withParam("room_id", room_id) + .build() + .toJsonResponse(); + + if (result.isFailure()) + throw new TikTokLiveRequestException("Unable to fetch gifts information's - "+result); + + var json = result.getContent(); + return giftsDataMapper.mapRoom(json); + } + public GiftsData.Response fetchGiftsData() { var proxyClientSettings = clientSettings.getHttpSettings().getProxyClientSettings(); if (proxyClientSettings.isEnabled()) { diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpOfflineClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpOfflineClient.java index c5fb961..76eaa94 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpOfflineClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpOfflineClient.java @@ -18,6 +18,11 @@ public class TikTokLiveHttpOfflineClient implements LiveHttpClient { return new GiftsData.Response("", List.of()); } + @Override + public GiftsData.Response fetchRoomGiftsData(String room_id) { + return new GiftsData.Response("", List.of()); + } + @Override public LiveUserData.Response fetchLiveUserData(LiveUserData.Request request) { return new LiveUserData.Response("", LiveUserData.UserStatus.Live, "offline_room_id", 0); @@ -42,4 +47,4 @@ public class TikTokLiveHttpOfflineClient implements LiveHttpClient { URI.create("https://example.live"), WebcastResponse.newBuilder().build()); } -} +} \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftsManager.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftsManager.java index 60e3b76..622500a 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftsManager.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/gifts/TikTokGiftsManager.java @@ -4,7 +4,7 @@ import io.github.jwdeveloper.tiktok.data.models.gifts.Gift; import io.github.jwdeveloper.tiktok.live.GiftsManager; import java.util.*; -import java.util.function.Predicate; +import java.util.function.*; import java.util.stream.Collectors; public class TikTokGiftsManager implements GiftsManager { @@ -12,7 +12,7 @@ public class TikTokGiftsManager implements GiftsManager { public TikTokGiftsManager(List giftList) { - giftsByIdIndex = giftList.stream().collect(Collectors.toConcurrentMap(Gift::getId, e -> e)); + giftsByIdIndex = giftList.stream().collect(Collectors.toConcurrentMap(Gift::getId, Function.identity())); } public void attachGift(Gift gift) { diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/GiftsDataMapper.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/GiftsDataMapper.java index 6f64425..26607a4 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/GiftsDataMapper.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/http/mappers/GiftsDataMapper.java @@ -22,15 +22,15 @@ */ package io.github.jwdeveloper.tiktok.http.mappers; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; +import com.google.gson.*; import io.github.jwdeveloper.tiktok.data.models.Picture; import io.github.jwdeveloper.tiktok.data.models.gifts.Gift; import io.github.jwdeveloper.tiktok.data.requests.GiftsData; -import java.util.ArrayList; +import java.util.List; public class GiftsDataMapper { + public GiftsData.Response map(String json) { var parsedJson = JsonParser.parseString(json); var jsonObject = parsedJson.getAsJsonObject(); @@ -42,7 +42,6 @@ public class GiftsDataMapper { return new GiftsData.Response(json, gifts); } - private Gift mapSingleGift(JsonElement jsonElement) { var jsonObject = jsonElement.getAsJsonObject(); @@ -52,4 +51,34 @@ public class GiftsDataMapper { var image = jsonObject.get("image").getAsString(); return new Gift(id, name, diamondCost, new Picture(image), jsonObject); } -} + + public GiftsData.Response mapRoom(String json) { + var parsedJson = JsonParser.parseString(json); + var jsonObject = parsedJson.getAsJsonObject(); + if (jsonObject.get("data") instanceof JsonObject data && data.get("gifts") instanceof JsonArray giftArray) { + var gifts = giftArray.asList().parallelStream() + .map(this::mapSingleRoomGift) + .toList(); + + return new GiftsData.Response(json, gifts); + } + return new GiftsData.Response("", List.of()); + } + + private Gift mapSingleRoomGift(JsonElement jsonElement) { + var jsonObject = jsonElement.getAsJsonObject(); + + var id = jsonObject.get("id").getAsInt(); + var name = jsonObject.get("name").getAsString(); + var diamondCost = jsonObject.get("diamond_count").getAsInt(); + Picture picture; + if (jsonObject.get("image") instanceof JsonObject image && image.get("url_list") instanceof JsonArray urls && !urls.isEmpty()) { + String url = urls.get(0).getAsString(); + if (url.endsWith(".webp")) + url = url.substring(0, url.length()-4)+"png"; + picture = new Picture(url); + } else + picture = Picture.empty(); + return new Gift(id, name, diamondCost, picture, jsonObject); + } +} \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketClient.java index d1c8a62..bfeffa1 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/websocket/TikTokWebSocketClient.java @@ -33,7 +33,6 @@ import org.java_websocket.client.WebSocketClient; import javax.net.ssl.*; import java.net.Proxy; import java.security.cert.X509Certificate; -import java.util.HashMap; public class TikTokWebSocketClient implements SocketClient { private final LiveClientSettings clientSettings; @@ -63,7 +62,7 @@ public class TikTokWebSocketClient implements SocketClient { messageHandler.handle(liveClient, connectionData.getWebcastResponse()); - var headers = new HashMap(); + var headers = clientSettings.getHttpSettings().getHeaders(); headers.put("Cookie", connectionData.getWebsocketCookies()); webSocketClient = new TikTokWebSocketListener(connectionData.getWebsocketUrl(), headers, diff --git a/pom.xml b/pom.xml index db1e1d7..129aa94 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ org.projectlombok lombok - 1.18.22 + 1.18.32 provided @@ -106,4 +106,4 @@ - + \ No newline at end of file