diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokEventListener.java b/API/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokEventListener.java index cf70bca..fcb2745 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokEventListener.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokEventListener.java @@ -38,9 +38,9 @@ import io.github.jwdeveloper.tiktok.live.LiveClient; *

- 2 parameters of (LiveClient, Class extending TikTokEvent) *

  * {@code
- * 	public static class CustomListener implements TikTokEventListener
+ * 	public static class CustomListener
  *  {
- *    @TikTokEventObserver
+ *      @TikTokEventObserver
  *      public void onError(LiveClient liveClient, TikTokErrorEvent event)
  *      {
  *      	System.out.println(event.getException().getMessage());
@@ -67,9 +67,7 @@ import io.github.jwdeveloper.tiktok.live.LiveClient;
  *  }
  *  
*/ -//TODO I think this interface can be removed, since we are using, -//annotation @TikTokEventHandler to check methods that are events -@Deprecated(forRemoval = true, since = "1.8.1 (This interface is not longer needed, please remove it from your class)") +@Deprecated(forRemoval = true, since = "1.8.1 (This interface is not longer needed, please remove it from your class) | Removing in 1.9.0") public interface TikTokEventListener { } \ No newline at end of file diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokEventListenerBase.java b/API/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokEventListenerBase.java index d21c272..136e624 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokEventListenerBase.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokEventListenerBase.java @@ -35,7 +35,7 @@ import io.github.jwdeveloper.tiktok.data.events.social.*; import io.github.jwdeveloper.tiktok.data.events.websocket.*; import io.github.jwdeveloper.tiktok.live.LiveClient; -public abstract class TikTokEventListenerBase implements TikTokEventListener +public abstract class TikTokEventListenerBase { public void onUnhandledSocial(LiveClient client, TikTokUnhandledSocialEvent event) {} diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveClient.java b/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveClient.java index 0c4cef9..82c07fe 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveClient.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveClient.java @@ -24,7 +24,6 @@ package io.github.jwdeveloper.tiktok.live; import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; import io.github.jwdeveloper.tiktok.listener.ListenersManager; -import io.github.jwdeveloper.tiktok.listener.TikTokEventListener; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -81,9 +80,7 @@ public interface LiveClient { LiveRoomInfo getRoomInfo(); /** - * Manage TikTokEventListener - * - * @see TikTokEventListener + * Manage TikTokEvent Listeners */ ListenersManager getListenersManager(); @@ -91,4 +88,4 @@ public interface LiveClient { * Logger */ Logger getLogger(); -} +} \ No newline at end of file diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/live/builder/LiveClientBuilder.java b/API/src/main/java/io/github/jwdeveloper/tiktok/live/builder/LiveClientBuilder.java index 8e343c9..5410dcf 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/live/builder/LiveClientBuilder.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/live/builder/LiveClientBuilder.java @@ -57,8 +57,7 @@ public interface LiveClientBuilder extends EventsBuilder { LiveClientBuilder configure(Consumer onConfigure); /** - * Adding events listener class, its fancy way to register events without using lamda method - * but actual method in class that implements TikTokEventListener + * Adds events listener class, its fancy way to register events without using lamda method * * @return */ @@ -92,4 +91,4 @@ public interface LiveClientBuilder extends EventsBuilder { * @return LiveClient object and connects to TikTok live asynchronously */ CompletableFuture buildAndConnectAsync(); -} +} \ No newline at end of file diff --git a/API/src/main/proto/enums.proto b/API/src/main/proto/enums.proto index b11ee20..0c021e6 100644 --- a/API/src/main/proto/enums.proto +++ b/API/src/main/proto/enums.proto @@ -155,8 +155,9 @@ enum MemberMessageAction { enum ControlAction { ControlActionUNKNOWN = 0; STREAM_PAUSED = 1; // Stream Paused by Host - STREAM_UNPAUSED = 2; + STREAM_UNPAUSED = 2; // Stream Unpaused by Host STREAM_ENDED = 3; // Stream Ended by Host + STREAM_SUSPENDED = 4; // Stream Ended by TikTok } enum LinkLayerMessageType 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 d106762..496e3c2 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveHttpClient.java @@ -107,6 +107,7 @@ public class TikTokLiveHttpClient implements LiveHttpClient return getGiftsData(); } + @Deprecated(since = "1.8.6", forRemoval = true) public GiftsData.Response getGiftsData() { var result = httpFactory.client(TIKTOK_GIFTS_URL) .build() diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/handlers/TikTokCommonEventHandler.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/handlers/TikTokCommonEventHandler.java index 3815283..0af7d4a 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/handlers/TikTokCommonEventHandler.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/handlers/TikTokCommonEventHandler.java @@ -46,7 +46,7 @@ public class TikTokCommonEventHandler var message = WebcastControlMessage.parseFrom(msg); return switch (message.getAction()) { case STREAM_PAUSED -> new TikTokLivePausedEvent(); - case STREAM_ENDED -> new TikTokLiveEndedEvent(); + case STREAM_ENDED, STREAM_SUSPENDED -> new TikTokLiveEndedEvent(); case STREAM_UNPAUSED -> new TikTokLiveUnpausedEvent(); default -> new TikTokUnhandledControlEvent(message); }; @@ -85,4 +85,4 @@ public class TikTokCommonEventHandler return List.of(new TikTokChestEvent(chest, msg)); } -} +} \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/handlers/TikTokRoomInfoEventHandler.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/handlers/TikTokRoomInfoEventHandler.java index c6a1f21..6540171 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/handlers/TikTokRoomInfoEventHandler.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/mappers/handlers/TikTokRoomInfoEventHandler.java @@ -58,6 +58,7 @@ public class TikTokRoomInfoEventHandler { @SneakyThrows public TikTokEvent handleUserRanking(byte[] msg) { var message = WebcastRoomUserSeqMessage.parseFrom(msg); + var currentViewers = (int) message.getTotal(); var totalUsers = message.getTotalUser(); var userRanking = message.getRanksListList().stream().map(RankingUser::new) .sorted((ru1, ru2) -> Integer.compare(ru2.getScore(), ru1.getScore())) @@ -65,6 +66,7 @@ public class TikTokRoomInfoEventHandler { return handleRoomInfo(tikTokRoomInfo -> { + tikTokRoomInfo.setViewersCount(currentViewers); tikTokRoomInfo.setTotalViewersCount(totalUsers); tikTokRoomInfo.updateRanking(userRanking); }); @@ -112,4 +114,4 @@ public class TikTokRoomInfoEventHandler { }); return MappingResult.of(message, List.of(event, roomInfoEvent)); } -} +} \ No newline at end of file diff --git a/README.md b/README.md index cfb0d45..64dea97 100644 --- a/README.md +++ b/README.md @@ -707,7 +707,7 @@ public static void main(String[] args) throws IOException { * - second must be class that extending TikTokEvent */ -public static class CustomListener implements TikTokEventListener { +public static class CustomListener { @TikTokEventObserver public void onLike(LiveClient liveClient, TikTokLikeEvent event) { @@ -754,4 +754,4 @@ public static class CustomListener implements TikTokEventListener { [Library documentation for contributors](https://github.com/jwdeveloper/TikTokLiveJava/wiki) -Your improvements are welcome! Feel free to open an issue or pull request. +Your improvements are welcome! Feel free to open an issue or pull request. \ No newline at end of file diff --git a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/LiveDataCollector.java b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/LiveDataCollector.java index 0532f24..6cdd483 100644 --- a/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/LiveDataCollector.java +++ b/extension-collector/src/main/java/io/github/jwdeveloper/tiktok/extension/collector/api/LiveDataCollector.java @@ -22,9 +22,7 @@ */ package io.github.jwdeveloper.tiktok.extension.collector.api; -import io.github.jwdeveloper.tiktok.listener.TikTokEventListener; - -public interface LiveDataCollector extends TikTokEventListener +public interface LiveDataCollector { -} +} \ No newline at end of file diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/api/LiveRecorder.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/api/LiveRecorder.java index 156b3e3..6043b98 100644 --- a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/api/LiveRecorder.java +++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/api/LiveRecorder.java @@ -22,9 +22,7 @@ */ package io.github.jwdeveloper.tiktok.extension.recorder.api; -import io.github.jwdeveloper.tiktok.listener.TikTokEventListener; - -public interface LiveRecorder extends TikTokEventListener { +public interface LiveRecorder { -} +} \ 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 516befe..1aceadb 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 @@ -30,7 +30,7 @@ import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; 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; -import io.github.jwdeveloper.tiktok.extension.recorder.impl.event.TikTokLiveRecorderStartedEvent; +import io.github.jwdeveloper.tiktok.extension.recorder.impl.event.*; import io.github.jwdeveloper.tiktok.live.LiveClient; import io.github.jwdeveloper.tiktok.models.ConnectionState; @@ -38,14 +38,17 @@ import java.io.*; import java.net.URI; import java.net.http.*; import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiConsumer; public class RecorderListener implements LiveRecorder { private final BiConsumer consumer; private final RecorderSettings settings; + private final AtomicBoolean token = new AtomicBoolean(); private DownloadData downloadData; - private Thread liveDownloadThread; + private CompletableFuture future; public RecorderListener(BiConsumer consumer) { this.consumer = consumer; @@ -74,60 +77,59 @@ public class RecorderListener implements LiveRecorder { if (isConnected() || downloadData.getDownloadLiveUrl().isEmpty()) return; - liveDownloadThread = new Thread(() -> { - try { - liveClient.getLogger().info("Recording started "+liveClient.getRoomInfo().getHostName()); - - HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(URI.create(downloadData.getFullUrl())).GET(); - for (var entry : LiveClientSettings.DefaultRequestHeaders().entrySet()) - requestBuilder.header(entry.getKey(), entry.getValue()); - HttpResponse serverResponse = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL) - .connectTimeout(Duration.ofSeconds(10)).build().send(requestBuilder.build(), HttpResponse.BodyHandlers.ofInputStream()); - - var file = settings.getOutputFile(); - file.getParentFile().mkdirs(); - file.createNewFile(); - - try ( - var in = serverResponse.body(); - var fos = new FileOutputStream(file, true) - ) { - byte[] dataBuffer = new byte[1024]; - int bytesRead; - while ((!settings.isStopOnDisconnect() || liveClient.getRoomInfo().getConnectionState() == ConnectionState.CONNECTED) && (bytesRead = in.read(dataBuffer)) != -1) { - fos.write(dataBuffer, 0, bytesRead); - fos.flush(); - } - } catch (IOException ignored) { - } finally { - liveClient.getLogger().severe("Stopped recording " + liveClient.getRoomInfo().getHostName()); - } - } catch (Exception e) { - e.printStackTrace(); - } - }); - - var recordingStartedEvent = new TikTokLiveRecorderStartedEvent(downloadData); + var recordingStartedEvent = new TikTokLiveRecorderStartedEvent(downloadData, settings); liveClient.publishEvent(recordingStartedEvent); if (recordingStartedEvent.isCanceled()) liveClient.getLogger().info("Recording cancelled"); else - liveDownloadThread.start(); + future = CompletableFuture.runAsync(() -> { + try { + liveClient.getLogger().info("Recording started "+liveClient.getRoomInfo().getHostName()); + + HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(URI.create(downloadData.getFullUrl())).GET(); + for (var entry : LiveClientSettings.DefaultRequestHeaders().entrySet()) + requestBuilder.header(entry.getKey(), entry.getValue()); + HttpResponse serverResponse = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL) + .connectTimeout(Duration.ofSeconds(10)).build().send(requestBuilder.build(), HttpResponse.BodyHandlers.ofInputStream()); + + var file = settings.getOutputFile(); + file.getParentFile().mkdirs(); + file.createNewFile(); + + try ( + var in = serverResponse.body(); + var fos = new FileOutputStream(file, true) + ) { + byte[] dataBuffer = new byte[1024]; + int bytesRead; + while (!token.get() && (!settings.isStopOnDisconnect() || liveClient.getRoomInfo().getConnectionState() == ConnectionState.CONNECTED) && (bytesRead = in.read(dataBuffer)) != -1) { + fos.write(dataBuffer, 0, bytesRead); + fos.flush(); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + liveClient.getLogger().info("Stopped recording " + liveClient.getRoomInfo().getHostName()); + liveClient.publishEvent(new TikTokLiveRecorderEndedEvent(settings)); + } + } catch (Exception e) { + e.printStackTrace(); + } + }); } @TikTokEventObserver private void onDisconnected(LiveClient liveClient, TikTokDisconnectedEvent event) { if (isConnected() && settings.isStopOnDisconnect()) - liveDownloadThread.interrupt(); + token.set(true); } @TikTokEventObserver private void onLiveEnded(LiveClient liveClient, TikTokLiveEndedEvent event) { if (isConnected()) - liveDownloadThread.interrupt(); + token.set(true); } - private DownloadData mapToDownloadData(String json) { try { var parsedJson = JsonParser.parseString(json); @@ -164,6 +166,6 @@ public class RecorderListener implements LiveRecorder { } private boolean isConnected() { - return liveDownloadThread != null && liveDownloadThread.isAlive(); + return future != null && !future.isDone(); } } \ No newline at end of file diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/event/TikTokLiveRecorderEndedEvent.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/event/TikTokLiveRecorderEndedEvent.java new file mode 100644 index 0000000..c3dfa0e --- /dev/null +++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/event/TikTokLiveRecorderEndedEvent.java @@ -0,0 +1,37 @@ +/* + * 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.extension.recorder.impl.event; + +import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; +import io.github.jwdeveloper.tiktok.extension.recorder.impl.data.*; +import lombok.*; + +@Getter +public class TikTokLiveRecorderEndedEvent extends TikTokEvent { + + private final RecorderSettings settings; + + public TikTokLiveRecorderEndedEvent(RecorderSettings settings) { + this.settings = settings; + } +} \ No newline at end of file diff --git a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/event/TikTokLiveRecorderStartedEvent.java b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/event/TikTokLiveRecorderStartedEvent.java index c5b8959..ee1079e 100644 --- a/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/event/TikTokLiveRecorderStartedEvent.java +++ b/extension-recorder/src/main/java/io/github/jwdeveloper/tiktok/extension/recorder/impl/event/TikTokLiveRecorderStartedEvent.java @@ -23,21 +23,21 @@ package io.github.jwdeveloper.tiktok.extension.recorder.impl.event; import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; -import io.github.jwdeveloper.tiktok.extension.recorder.impl.data.DownloadData; +import io.github.jwdeveloper.tiktok.extension.recorder.impl.data.*; import lombok.AllArgsConstructor; -import lombok.Data; import lombok.Getter; import lombok.Setter; @AllArgsConstructor @Getter public class TikTokLiveRecorderStartedEvent extends TikTokEvent { - DownloadData downloadData; + private final DownloadData downloadData; + private final RecorderSettings settings; - @Setter - boolean canceled; + @Setter boolean canceled; - public TikTokLiveRecorderStartedEvent(DownloadData downloadData) { + public TikTokLiveRecorderStartedEvent(DownloadData downloadData, RecorderSettings settings) { this.downloadData = downloadData; + this.settings = settings; } -} +} \ No newline at end of file