From d679bb65ca7c8c7c0fc1fa8697382e20413dd023 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sun, 10 Jan 2021 19:58:17 +0100 Subject: [PATCH 01/17] Revert "Use CompletableFuture.runAsync instead of creating new Threads" This reverts commit 53684668abf1af1b72a87a2e409e8449d30d4d8c. --- .../java/ctbrec/ui/CamrecApplication.java | 6 +- .../java/ctbrec/ui/PreviewPopupHandler.java | 7 +- .../ui/StreamSourceSelectionDialog.java | 3 +- client/src/main/java/ctbrec/ui/TipDialog.java | 3 +- .../src/main/java/ctbrec/ui/TokenLabel.java | 3 +- .../ui/action/CheckModelAccountAction.java | 4 +- .../ctbrec/ui/action/EditNotesAction.java | 5 +- .../ctbrec/ui/action/ModelMassEditAction.java | 11 ++- .../ctbrec/ui/action/OpenRecordingsDir.java | 3 +- .../ui/action/ToggleRecordingAction.java | 6 +- .../ui/controls/SearchPopoverTreeList.java | 28 ++++--- .../ctbrec/ui/controls/StreamPreview.java | 5 +- .../main/java/ctbrec/ui/controls/Toast.java | 6 +- .../src/main/java/ctbrec/ui/news/NewsTab.java | 17 ++-- .../ui/sites/bonga/BongaCamsSiteUi.java | 18 ++--- .../ui/sites/camsoda/CamsodaShowsTab.java | 77 ++++++++++--------- .../ui/sites/jasmin/LiveJasminSiteUi.java | 5 +- .../sites/myfreecams/MyFreeCamsTableTab.java | 5 +- .../ctbrec/ui/sites/showup/ShowupSiteUi.java | 5 +- .../java/ctbrec/ui/tabs/RecordingsTab.java | 25 +++--- .../main/java/ctbrec/ui/tabs/ThumbCell.java | 8 +- 21 files changed, 130 insertions(+), 120 deletions(-) diff --git a/client/src/main/java/ctbrec/ui/CamrecApplication.java b/client/src/main/java/ctbrec/ui/CamrecApplication.java index 1e049d2a..1e860240 100644 --- a/client/src/main/java/ctbrec/ui/CamrecApplication.java +++ b/client/src/main/java/ctbrec/ui/CamrecApplication.java @@ -16,7 +16,6 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -478,7 +477,7 @@ public class CamrecApplication extends Application { } private void checkForUpdates() { - CompletableFuture.runAsync(() -> { + Thread updateCheck = new Thread(() -> { String url = "https://pastebin.com/raw/mUxtKzyB"; Request request = new Request.Builder().url(url).build(); try (Response response = httpClient.execute(request)) { @@ -505,6 +504,9 @@ public class CamrecApplication extends Application { LOG.warn("Update check failed: {}", e.getMessage()); } }); + updateCheck.setName("Update Check"); + updateCheck.setDaemon(true); + updateCheck.start(); } public static Version getVersion() throws IOException { diff --git a/client/src/main/java/ctbrec/ui/PreviewPopupHandler.java b/client/src/main/java/ctbrec/ui/PreviewPopupHandler.java index 5b8434a2..5c05eb98 100644 --- a/client/src/main/java/ctbrec/ui/PreviewPopupHandler.java +++ b/client/src/main/java/ctbrec/ui/PreviewPopupHandler.java @@ -1,7 +1,6 @@ package ctbrec.ui; import java.util.Objects; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -146,7 +145,7 @@ public class PreviewPopupHandler implements EventHandler { } private void createTimerThread() { - CompletableFuture.runAsync(() -> { + Thread timerThread = new Thread(() -> { while(true) { openCountdown--; if(openCountdown == 0) { @@ -182,5 +181,9 @@ public class PreviewPopupHandler implements EventHandler { } } }); + timerThread.setDaemon(true); + timerThread.setPriority(Thread.MIN_PRIORITY); + timerThread.setName("PreviewPopupTimer"); + timerThread.start(); } } diff --git a/client/src/main/java/ctbrec/ui/StreamSourceSelectionDialog.java b/client/src/main/java/ctbrec/ui/StreamSourceSelectionDialog.java index 42c7c854..2aa64e8f 100644 --- a/client/src/main/java/ctbrec/ui/StreamSourceSelectionDialog.java +++ b/client/src/main/java/ctbrec/ui/StreamSourceSelectionDialog.java @@ -4,7 +4,6 @@ import java.io.InputStream; import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Function; @@ -62,7 +61,7 @@ public class StreamSourceSelectionDialog { } }); selectStreamSource.setOnFailed(e -> onFail.apply(selectStreamSource.getException())); - CompletableFuture.runAsync(selectStreamSource); + new Thread(selectStreamSource).start(); } private static class BestStreamSource extends StreamSource { diff --git a/client/src/main/java/ctbrec/ui/TipDialog.java b/client/src/main/java/ctbrec/ui/TipDialog.java index 5c1b8ecd..459d8d8a 100644 --- a/client/src/main/java/ctbrec/ui/TipDialog.java +++ b/client/src/main/java/ctbrec/ui/TipDialog.java @@ -2,7 +2,6 @@ package ctbrec.ui; import java.text.DecimalFormat; import java.util.Objects; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.slf4j.Logger; @@ -79,7 +78,7 @@ public class TipDialog extends TextInputDialog { } } }; - CompletableFuture.runAsync(task); + new Thread(task).start(); } private void showErrorDialog(Throwable throwable) { diff --git a/client/src/main/java/ctbrec/ui/TokenLabel.java b/client/src/main/java/ctbrec/ui/TokenLabel.java index 6a5cd5c8..917dad12 100644 --- a/client/src/main/java/ctbrec/ui/TokenLabel.java +++ b/client/src/main/java/ctbrec/ui/TokenLabel.java @@ -3,7 +3,6 @@ package ctbrec.ui; import java.text.DecimalFormat; import java.util.Map; import java.util.Objects; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.slf4j.Logger; @@ -84,6 +83,6 @@ public class TokenLabel extends Label { } } }; - CompletableFuture.runAsync(task); + new Thread(task).start(); } } diff --git a/client/src/main/java/ctbrec/ui/action/CheckModelAccountAction.java b/client/src/main/java/ctbrec/ui/action/CheckModelAccountAction.java index cad69b2a..c9bab392 100644 --- a/client/src/main/java/ctbrec/ui/action/CheckModelAccountAction.java +++ b/client/src/main/java/ctbrec/ui/action/CheckModelAccountAction.java @@ -3,7 +3,6 @@ package ctbrec.ui.action; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -32,7 +31,7 @@ public class CheckModelAccountAction { public void execute(Predicate filter) { String buttonText = b.getText(); b.setDisable(true); - CompletableFuture.runAsync(() -> { + Runnable checker = (() -> { List deletedAccounts = new ArrayList<>(); try { List models = recorder.getModels().stream() // @@ -71,5 +70,6 @@ public class CheckModelAccountAction { }); } }); + new Thread(checker).start(); } } diff --git a/client/src/main/java/ctbrec/ui/action/EditNotesAction.java b/client/src/main/java/ctbrec/ui/action/EditNotesAction.java index e55b77c0..2a23edec 100644 --- a/client/src/main/java/ctbrec/ui/action/EditNotesAction.java +++ b/client/src/main/java/ctbrec/ui/action/EditNotesAction.java @@ -2,7 +2,6 @@ package ctbrec.ui.action; import java.io.IOException; import java.util.Optional; -import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,7 +30,7 @@ public class EditNotesAction { public void execute() { source.setCursor(Cursor.WAIT); - CompletableFuture.runAsync(() -> Platform.runLater(() -> { + new Thread(() -> Platform.runLater(() -> { String notes = Config.getInstance().getSettings().modelNotes.getOrDefault(model.getUrl(), ""); Optional newNotes = Dialogs.showTextInput(source.getScene(), "Model Notes", "Notes for " + model.getName(), notes); newNotes.ifPresent(n -> { @@ -48,6 +47,6 @@ public class EditNotesAction { }); table.refresh(); source.setCursor(Cursor.DEFAULT); - })); + })).start(); } } diff --git a/client/src/main/java/ctbrec/ui/action/ModelMassEditAction.java b/client/src/main/java/ctbrec/ui/action/ModelMassEditAction.java index f6da2d5d..70c6dfc1 100644 --- a/client/src/main/java/ctbrec/ui/action/ModelMassEditAction.java +++ b/client/src/main/java/ctbrec/ui/action/ModelMassEditAction.java @@ -2,7 +2,11 @@ package ctbrec.ui.action; import java.util.List; import java.util.Objects; -import java.util.concurrent.CompletableFuture; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import ctbrec.Model; @@ -12,6 +16,9 @@ import javafx.scene.Node; public class ModelMassEditAction { + static BlockingQueue queue = new LinkedBlockingQueue<>(); + static ExecutorService threadPool = new ThreadPoolExecutor(2, 10, 10, TimeUnit.MINUTES, queue); + protected List models; protected Consumer action; protected Node source; @@ -35,7 +42,7 @@ public class ModelMassEditAction { Consumer cb = Objects.requireNonNull(callback, "Callback is null, call execute() instead"); source.setCursor(Cursor.WAIT); for (Model model : models) { - CompletableFuture.runAsync(() -> { + threadPool.submit(() -> { action.accept(model); cb.accept(model); Platform.runLater(() -> source.setCursor(Cursor.DEFAULT)); diff --git a/client/src/main/java/ctbrec/ui/action/OpenRecordingsDir.java b/client/src/main/java/ctbrec/ui/action/OpenRecordingsDir.java index b7d8d37c..4ef67fd0 100644 --- a/client/src/main/java/ctbrec/ui/action/OpenRecordingsDir.java +++ b/client/src/main/java/ctbrec/ui/action/OpenRecordingsDir.java @@ -2,7 +2,6 @@ package ctbrec.ui.action; import java.io.File; import java.time.Instant; -import java.util.concurrent.CompletableFuture; import ctbrec.Config; import ctbrec.Model; @@ -27,7 +26,7 @@ public class OpenRecordingsDir { File fileForRecording = Config.getInstance().getFileForRecording(selectedModel, ".mp4", Instant.now()); final File dir = getModelDirectory(fileForRecording); if (dir.exists()) { - CompletableFuture.runAsync(() -> DesktopIntegration.open(dir)); + new Thread(() -> DesktopIntegration.open(dir)).start(); } else { Dialogs.showError(source.getScene(), "Directory does not exist", "There are no recordings for this model", null); } diff --git a/client/src/main/java/ctbrec/ui/action/ToggleRecordingAction.java b/client/src/main/java/ctbrec/ui/action/ToggleRecordingAction.java index f15f3e20..6e9fc155 100644 --- a/client/src/main/java/ctbrec/ui/action/ToggleRecordingAction.java +++ b/client/src/main/java/ctbrec/ui/action/ToggleRecordingAction.java @@ -1,7 +1,5 @@ package ctbrec.ui.action; -import java.util.concurrent.CompletableFuture; - import ctbrec.recorder.Recorder; import ctbrec.ui.controls.Dialogs; import javafx.application.Platform; @@ -22,7 +20,7 @@ public class ToggleRecordingAction { public void execute() { toggleButton.setCursor(Cursor.WAIT); - CompletableFuture.runAsync(() -> { + Thread t = new Thread(() -> { try { if (pause) { recorder.pause(); @@ -38,5 +36,7 @@ public class ToggleRecordingAction { Platform.runLater(() -> toggleButton.setCursor(Cursor.DEFAULT)); } }); + t.setDaemon(true); + t.start(); } } diff --git a/client/src/main/java/ctbrec/ui/controls/SearchPopoverTreeList.java b/client/src/main/java/ctbrec/ui/controls/SearchPopoverTreeList.java index aa8131b2..966a2b21 100644 --- a/client/src/main/java/ctbrec/ui/controls/SearchPopoverTreeList.java +++ b/client/src/main/java/ctbrec/ui/controls/SearchPopoverTreeList.java @@ -31,6 +31,13 @@ */ package ctbrec.ui.controls; +import java.net.URL; +import java.util.Objects; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import ctbrec.Model; import ctbrec.recorder.Recorder; import ctbrec.ui.action.PlayAction; @@ -40,18 +47,15 @@ import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.scene.Cursor; import javafx.scene.Node; -import javafx.scene.control.*; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.Skin; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; import javafx.scene.shape.Rectangle; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.URL; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; /** * Popover page that displays a list of samples and sample categories for a given SampleCategory. @@ -163,7 +167,7 @@ public class SearchPopoverTreeList extends PopoverTreeList implements Pop follow = new Button("Follow"); follow.setOnAction(evt -> { setCursor(Cursor.WAIT); - CompletableFuture.runAsync(new Task() { + new Thread(new Task() { @Override protected Boolean call() throws Exception { model.getSite().login(); @@ -179,12 +183,12 @@ public class SearchPopoverTreeList extends PopoverTreeList implements Pop } Platform.runLater(() -> setCursor(Cursor.DEFAULT)); } - }); + }).start(); }); record = new Button("Record"); record.setOnAction(evt -> { setCursor(Cursor.WAIT); - CompletableFuture.runAsync(new Task() { + new Thread(new Task() { @Override protected Void call() throws Exception { recorder.addModel(model); @@ -195,7 +199,7 @@ public class SearchPopoverTreeList extends PopoverTreeList implements Pop protected void done() { Platform.runLater(() -> setCursor(Cursor.DEFAULT)); } - }); + }).start(); }); getChildren().addAll(thumb, title, follow, record); diff --git a/client/src/main/java/ctbrec/ui/controls/StreamPreview.java b/client/src/main/java/ctbrec/ui/controls/StreamPreview.java index 249eda45..9b1eb20e 100644 --- a/client/src/main/java/ctbrec/ui/controls/StreamPreview.java +++ b/client/src/main/java/ctbrec/ui/controls/StreamPreview.java @@ -3,7 +3,6 @@ package ctbrec.ui.controls; import java.io.InterruptedIOException; import java.util.Collections; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -158,14 +157,14 @@ public class StreamPreview extends StackPane { running = false; MediaPlayer old = videoPlayer; Future oldFuture = future; - CompletableFuture.runAsync(() -> { + new Thread(() -> { if(oldFuture != null && !oldFuture.isDone()) { oldFuture.cancel(true); } if(old != null) { old.dispose(); } - }); + }).start(); } private void onError(MediaPlayer videoPlayer) { diff --git a/client/src/main/java/ctbrec/ui/controls/Toast.java b/client/src/main/java/ctbrec/ui/controls/Toast.java index c74b7986..e622013d 100644 --- a/client/src/main/java/ctbrec/ui/controls/Toast.java +++ b/client/src/main/java/ctbrec/ui/controls/Toast.java @@ -1,7 +1,5 @@ package ctbrec.ui.controls; -import java.util.concurrent.CompletableFuture; - import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; @@ -38,7 +36,7 @@ public final class Toast { KeyFrame fadeInKey1 = new KeyFrame(Duration.millis(fadeInDelay), new KeyValue(toastStage.getScene().getRoot().opacityProperty(), 1)); fadeInTimeline.getKeyFrames().add(fadeInKey1); fadeInTimeline.setOnFinished((ae) -> { - CompletableFuture.runAsync(() -> { + new Thread(() -> { try { Thread.sleep(toastDelay); } catch (InterruptedException e) { @@ -49,7 +47,7 @@ public final class Toast { fadeOutTimeline.getKeyFrames().add(fadeOutKey1); fadeOutTimeline.setOnFinished((aeb) -> toastStage.close()); fadeOutTimeline.play(); - }); + }).start(); }); fadeInTimeline.play(); } diff --git a/client/src/main/java/ctbrec/ui/news/NewsTab.java b/client/src/main/java/ctbrec/ui/news/NewsTab.java index 712b5a15..a5f11d8e 100644 --- a/client/src/main/java/ctbrec/ui/news/NewsTab.java +++ b/client/src/main/java/ctbrec/ui/news/NewsTab.java @@ -1,16 +1,7 @@ package ctbrec.ui.news; -import static ctbrec.io.HttpConstants.*; - -import java.io.IOException; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; - -import org.json.JSONObject; - import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.Moshi; - import ctbrec.io.HttpException; import ctbrec.ui.CamrecApplication; import ctbrec.ui.controls.Dialogs; @@ -23,6 +14,12 @@ import javafx.scene.control.Tab; import javafx.scene.layout.VBox; import okhttp3.Request; import okhttp3.Response; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.Objects; + +import static ctbrec.io.HttpConstants.USER_AGENT; public class NewsTab extends Tab implements TabSelectionListener { private static final String ACCESS_TOKEN = "a2804d73a89951a22e0f8483a6fcec8943afd88b7ba17c459c095aa9e6f94fd0"; @@ -39,7 +36,7 @@ public class NewsTab extends Tab implements TabSelectionListener { @Override public void selected() { - CompletableFuture.runAsync(this::loadToots); + new Thread(this::loadToots).start(); } private void loadToots() { diff --git a/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsSiteUi.java b/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsSiteUi.java index a391bc20..a75ab316 100644 --- a/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsSiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsSiteUi.java @@ -1,19 +1,17 @@ package ctbrec.ui.sites.bonga; -import java.io.IOException; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.LinkedBlockingQueue; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import ctbrec.sites.bonga.BongaCams; import ctbrec.sites.bonga.BongaCamsHttpClient; import ctbrec.ui.controls.Dialogs; import ctbrec.ui.sites.AbstractSiteUi; import ctbrec.ui.sites.ConfigUI; import ctbrec.ui.tabs.TabProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; public class BongaCamsSiteUi extends AbstractSiteUi { @@ -46,7 +44,7 @@ public class BongaCamsSiteUi extends AbstractSiteUi { } else { BlockingQueue queue = new LinkedBlockingQueue<>(); try { - CompletableFuture.runAsync(() -> { + new Thread(() -> { // login with external browser window try { new BongaCamsElectronLoginDialog(bongaCams.getHttpClient().getCookieJar()); @@ -61,7 +59,7 @@ public class BongaCamsSiteUi extends AbstractSiteUi { Thread.currentThread().interrupt(); LOG.error("Error while signaling termination", e); } - }); + }).start(); queue.take(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); diff --git a/client/src/main/java/ctbrec/ui/sites/camsoda/CamsodaShowsTab.java b/client/src/main/java/ctbrec/ui/sites/camsoda/CamsodaShowsTab.java index d4e0d342..68049c52 100644 --- a/client/src/main/java/ctbrec/ui/sites/camsoda/CamsodaShowsTab.java +++ b/client/src/main/java/ctbrec/ui/sites/camsoda/CamsodaShowsTab.java @@ -1,32 +1,5 @@ package ctbrec.ui.sites.camsoda; -import ctbrec.Model; -import ctbrec.recorder.Recorder; -import ctbrec.sites.camsoda.Camsoda; -import ctbrec.ui.AutosizeAlert; -import ctbrec.ui.DesktopIntegration; -import ctbrec.ui.SiteUiFactory; -import ctbrec.ui.tabs.TabSelectionListener; -import javafx.application.Platform; -import javafx.beans.value.ChangeListener; -import javafx.concurrent.Task; -import javafx.geometry.Insets; -import javafx.scene.Cursor; -import javafx.scene.Node; -import javafx.scene.control.*; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.GridPane; -import javafx.scene.text.Font; -import javafx.scene.text.FontWeight; -import okhttp3.Request; -import okhttp3.Response; -import org.json.JSONArray; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -40,7 +13,41 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; -import java.util.concurrent.CompletableFuture; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ctbrec.Model; +import ctbrec.recorder.Recorder; +import ctbrec.sites.camsoda.Camsoda; +import ctbrec.ui.AutosizeAlert; +import ctbrec.ui.DesktopIntegration; +import ctbrec.ui.SiteUiFactory; +import ctbrec.ui.tabs.TabSelectionListener; +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.concurrent.Task; +import javafx.geometry.Insets; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.Tab; +import javafx.scene.control.TitledPane; +import javafx.scene.control.Tooltip; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.GridPane; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import okhttp3.Request; +import okhttp3.Response; public class CamsodaShowsTab extends Tab implements TabSelectionListener { @@ -129,7 +136,7 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener { }); } }; - CompletableFuture.runAsync(task); + new Thread(task).start(); } @Override @@ -195,7 +202,7 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener { private void follow(Model model) { setCursor(Cursor.WAIT); - CompletableFuture.runAsync(() -> { + new Thread(() -> { try { SiteUiFactory.getUi(model.getSite()).login(); model.follow(); @@ -207,12 +214,12 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener { setCursor(Cursor.DEFAULT); }); } - }); + }).start(); } private void record(Model model) { setCursor(Cursor.WAIT); - CompletableFuture.runAsync(() -> { + new Thread(() -> { try { recorder.addModel(model); } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { @@ -222,11 +229,11 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener { setCursor(Cursor.DEFAULT); }); } - }); + }).start(); } private void loadImage(Model model, ImageView thumb) { - CompletableFuture.runAsync(() -> { + new Thread(() -> { try { String url = camsoda.getBaseUrl() + "/api/v1/user/" + model.getName(); Request detailRequest = new Request.Builder().url(url).build(); @@ -263,7 +270,7 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener { } catch (Exception e) { LOG.error("Couldn't load model details", e); } - }); + }).start(); } private Node createLabel(String string, boolean bold) { diff --git a/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminSiteUi.java b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminSiteUi.java index 6ae87f34..5975a489 100644 --- a/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminSiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminSiteUi.java @@ -2,7 +2,6 @@ package ctbrec.ui.sites.jasmin; import java.io.IOException; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -56,7 +55,7 @@ public class LiveJasminSiteUi extends AbstractSiteUi { lastLoginTime = System.currentTimeMillis(); BlockingQueue queue = new LinkedBlockingQueue<>(); - CompletableFuture.runAsync(() -> { + new Thread (() -> { // login with external browser window try { new LiveJasminElectronLoginDialog(liveJasmin.getHttpClient().getCookieJar()); @@ -70,7 +69,7 @@ public class LiveJasminSiteUi extends AbstractSiteUi { } catch (InterruptedException e) { LOG.error("Error while signaling termination", e); } - }); + }).start(); try { queue.take(); diff --git a/client/src/main/java/ctbrec/ui/sites/myfreecams/MyFreeCamsTableTab.java b/client/src/main/java/ctbrec/ui/sites/myfreecams/MyFreeCamsTableTab.java index 427a4174..c09865fb 100644 --- a/client/src/main/java/ctbrec/ui/sites/myfreecams/MyFreeCamsTableTab.java +++ b/client/src/main/java/ctbrec/ui/sites/myfreecams/MyFreeCamsTableTab.java @@ -18,7 +18,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; @@ -354,7 +353,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener { if (Objects.equals(System.getenv("CTBREC_DEV"), "1")) { MenuItem debug = new MenuItem("debug"); - debug.setOnAction(e -> CompletableFuture.runAsync(() -> { + debug.setOnAction(e -> new Thread(() -> { for (Model m : selectedModels) { try { List sources = m.getStreamSources(); @@ -366,7 +365,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener { LOG.error("Couldn't get stream sources", e1); } } - })); + }).start()); menu.getItems().add(debug); } diff --git a/client/src/main/java/ctbrec/ui/sites/showup/ShowupSiteUi.java b/client/src/main/java/ctbrec/ui/sites/showup/ShowupSiteUi.java index 7cdd781e..3ab05d27 100644 --- a/client/src/main/java/ctbrec/ui/sites/showup/ShowupSiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/showup/ShowupSiteUi.java @@ -2,7 +2,6 @@ package ctbrec.ui.sites.showup; import java.io.IOException; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.LinkedBlockingQueue; import org.slf4j.Logger; @@ -47,7 +46,7 @@ public class ShowupSiteUi extends AbstractSiteUi { } else { BlockingQueue queue = new LinkedBlockingQueue<>(); try { - CompletableFuture.runAsync(() -> { + new Thread(() -> { // login with external browser window try { new ShowupElectronLoginDialog(site.getHttpClient().getCookieJar()); @@ -62,7 +61,7 @@ public class ShowupSiteUi extends AbstractSiteUi { LOG.error("Error while signaling termination", e); Thread.currentThread().interrupt(); } - }); + }).start(); queue.take(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); diff --git a/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java b/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java index 612e985e..beba4d80 100644 --- a/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java @@ -17,7 +17,6 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -497,9 +496,9 @@ public class RecordingsTab extends Tab implements TabSelectionListener { private void openContactSheet(JavaFxRecording recording) { if (config.getSettings().localRecording) { - recording.getContactSheet().ifPresent(f -> CompletableFuture.runAsync(() -> DesktopIntegration.open(f))); + recording.getContactSheet().ifPresent(f -> new Thread(() -> DesktopIntegration.open(f)).start()); } else { - recording.getContactSheet().ifPresent(f -> CompletableFuture.runAsync(() -> { + recording.getContactSheet().ifPresent(f -> new Thread(() -> { File target; try { target = File.createTempFile("cs_", ".jpg"); @@ -517,7 +516,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) { Dialogs.showError(getTabPane().getScene(), "Download Error", "An error occurred while downloading the contact sheet", e); } - })); + }).start()); } } @@ -527,7 +526,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { Optional newNote = Dialogs.showTextInput(source.getScene(), "Recording Notes", "", notes); if (newNote.isPresent()) { table.setCursor(Cursor.WAIT); - CompletableFuture.runAsync(() -> { + Thread backgroundThread = new Thread(() -> { List exceptions = new ArrayList<>(); try { recording.setNote(newNote.get()); @@ -543,12 +542,13 @@ public class RecordingsTab extends Tab implements TabSelectionListener { }); } }); + backgroundThread.start(); } } private void pin(List recordings) { table.setCursor(Cursor.WAIT); - CompletableFuture.runAsync(() -> { + Thread backgroundThread = new Thread(() -> { List exceptions = new ArrayList<>(); try { for (JavaFxRecording javaFxRecording : recordings) { @@ -569,11 +569,12 @@ public class RecordingsTab extends Tab implements TabSelectionListener { }); } }); + backgroundThread.start(); } private void unpin(List recordings) { table.setCursor(Cursor.WAIT); - CompletableFuture.runAsync(() -> { + Thread backgroundThread = new Thread(() -> { List exceptions = new ArrayList<>(); try { for (JavaFxRecording javaFxRecording : recordings) { @@ -594,6 +595,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { }); } }); + backgroundThread.start(); } private void jumpToNextModel(KeyCode code) { @@ -644,11 +646,11 @@ public class RecordingsTab extends Tab implements TabSelectionListener { private void onOpenDirectory(JavaFxRecording first) { File tsFile = first.getAbsoluteFile(); - CompletableFuture.runAsync(() -> DesktopIntegration.open(tsFile.getParent())); + new Thread(() -> DesktopIntegration.open(tsFile.getParent())).start(); } private void triggerPostProcessing(List recs) { - CompletableFuture.runAsync(() -> { + new Thread(() -> { for (JavaFxRecording rec : recs) { try { recorder.rerunPostProcessing(rec.getDelegate()); @@ -657,7 +659,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { LOG.error("Error while starting post-processing", e1); } } - }); + }).start(); } private void download(Recording recording) { @@ -793,7 +795,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { } private void deleteAsync(List recordings) { - CompletableFuture.runAsync(() -> { + Thread deleteThread = new Thread(() -> { recordingsLock.lock(); try { List deleted = new ArrayList<>(); @@ -819,6 +821,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { Platform.runLater(() -> table.setCursor(Cursor.DEFAULT)); } }); + deleteThread.start(); } public void saveState() { diff --git a/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java b/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java index 1c6455b9..25a36772 100644 --- a/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java +++ b/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java @@ -512,7 +512,7 @@ public class ThumbCell extends StackPane { void pauseResumeAction(boolean pause) { setCursor(Cursor.WAIT); - CompletableFuture.runAsync(() -> { + new Thread(() -> { try { if (pause) { recorder.suspendRecording(model); @@ -532,11 +532,11 @@ public class ThumbCell extends StackPane { } finally { Platform.runLater(() -> setCursor(Cursor.DEFAULT)); } - }); + }).start(); } private void startStopActionAsync(Model model, boolean start) { - CompletableFuture.runAsync(() -> { + new Thread(() -> { try { if (start) { recorder.addModel(model); @@ -552,7 +552,7 @@ public class ThumbCell extends StackPane { } finally { Platform.runLater(() -> setCursor(Cursor.DEFAULT)); } - }); + }).start(); } CompletableFuture follow(boolean follow) { From 3c1e0eea96e235d5d425e012182cee45ee7eff40 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sun, 10 Jan 2021 20:13:15 +0100 Subject: [PATCH 02/17] Add a global ThreadPool --- .../main/java/ctbrec/GlobalThreadPool.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 client/src/main/java/ctbrec/GlobalThreadPool.java diff --git a/client/src/main/java/ctbrec/GlobalThreadPool.java b/client/src/main/java/ctbrec/GlobalThreadPool.java new file mode 100644 index 00000000..aaeccf92 --- /dev/null +++ b/client/src/main/java/ctbrec/GlobalThreadPool.java @@ -0,0 +1,32 @@ +package ctbrec; + +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public class GlobalThreadPool { + + private static ExecutorService threadPool = Executors.newCachedThreadPool(r -> { + Thread t = new Thread(r); + t.setDaemon(true); + t.setName("GlobalWorker-" + UUID.randomUUID().toString().substring(0, 8)); + return t; + }); + + private GlobalThreadPool() { + } + + public static Future submit(Runnable runnable) { // NOSONAR + return threadPool.submit(runnable); + } + + public static Future submit(Callable callable) { + return threadPool.submit(callable); + } + + public static ExecutorService get() { + return threadPool; + } +} From 4f7060dfef3ca2c3e223eb25f40bf9e543610b19 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sun, 10 Jan 2021 20:14:36 +0100 Subject: [PATCH 03/17] Use GlobalThreadPool in conjunction with CompletableFuture --- .../ui/action/RemoveTimeLimitAction.java | 3 +- .../ctbrec/ui/action/SetStopDateAction.java | 3 +- .../java/ctbrec/ui/settings/SettingsTab.java | 4 +-- .../ui/sites/camsoda/CamsodaShowsTab.java | 29 +++++++++---------- .../main/java/ctbrec/ui/tabs/ThumbCell.java | 5 ++-- .../main/java/ctbrec/ui/tabs/UpdateTab.java | 5 ++-- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/client/src/main/java/ctbrec/ui/action/RemoveTimeLimitAction.java b/client/src/main/java/ctbrec/ui/action/RemoveTimeLimitAction.java index fd3e6460..4e86a3c7 100644 --- a/client/src/main/java/ctbrec/ui/action/RemoveTimeLimitAction.java +++ b/client/src/main/java/ctbrec/ui/action/RemoveTimeLimitAction.java @@ -6,6 +6,7 @@ import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.util.concurrent.CompletableFuture; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.recorder.Recorder; import ctbrec.ui.controls.Dialogs; @@ -36,6 +37,6 @@ public class RemoveTimeLimitAction { Dialogs.showError(source.getScene(), "Error", "Couln't remove stop date", e); return false; } - }).whenComplete((r,e) -> source.setCursor(Cursor.DEFAULT)); + }, GlobalThreadPool.get()).whenComplete((r,e) -> source.setCursor(Cursor.DEFAULT)); } } diff --git a/client/src/main/java/ctbrec/ui/action/SetStopDateAction.java b/client/src/main/java/ctbrec/ui/action/SetStopDateAction.java index cd83590e..9e9b6e2e 100644 --- a/client/src/main/java/ctbrec/ui/action/SetStopDateAction.java +++ b/client/src/main/java/ctbrec/ui/action/SetStopDateAction.java @@ -13,6 +13,7 @@ import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.SubsequentAction; import ctbrec.recorder.Recorder; @@ -82,7 +83,7 @@ public class SetStopDateAction { } } return true; - }).whenComplete((r, e) -> { + }, GlobalThreadPool.get()).whenComplete((r, e) -> { source.setCursor(Cursor.DEFAULT); if (e != null) { LOG.error("Error", e); diff --git a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java index 17d6bf2f..ed3452b9 100644 --- a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java +++ b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java @@ -9,13 +9,13 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Config; +import ctbrec.GlobalThreadPool; import ctbrec.Hmac; import ctbrec.Settings; import ctbrec.Settings.DirectoryStructure; @@ -436,7 +436,7 @@ public class SettingsTab extends Tab implements TabSelectionListener { } public void saveConfig() { - CompletableFuture.runAsync(() -> { + GlobalThreadPool.submit(() -> { try { Config.getInstance().save(); } catch (IOException e) { diff --git a/client/src/main/java/ctbrec/ui/sites/camsoda/CamsodaShowsTab.java b/client/src/main/java/ctbrec/ui/sites/camsoda/CamsodaShowsTab.java index 68049c52..206eb205 100644 --- a/client/src/main/java/ctbrec/ui/sites/camsoda/CamsodaShowsTab.java +++ b/client/src/main/java/ctbrec/ui/sites/camsoda/CamsodaShowsTab.java @@ -19,6 +19,7 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.recorder.Recorder; import ctbrec.sites.camsoda.Camsoda; @@ -136,7 +137,7 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener { }); } }; - new Thread(task).start(); + GlobalThreadPool.submit(task); } @Override @@ -180,17 +181,17 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener { grid.add(createLabel(formatter.format(endTime), false), 1, 1); Button record = new Button("Record Model"); record.setTooltip(new Tooltip(record.getText())); - record.setOnAction((evt) -> record(model)); + record.setOnAction(evt -> record(model)); grid.add(record, 1, 2); GridPane.setMargin(record, new Insets(10)); Button follow = new Button("Follow"); follow.setTooltip(new Tooltip(follow.getText())); - follow.setOnAction((evt) -> follow(model)); + follow.setOnAction(evt -> follow(model)); grid.add(follow, 1, 3); GridPane.setMargin(follow, new Insets(10)); Button openInBrowser = new Button("Open in Browser"); openInBrowser.setTooltip(new Tooltip(openInBrowser.getText())); - openInBrowser.setOnAction((evt) -> DesktopIntegration.open(model.getUrl())); + openInBrowser.setOnAction(evt -> DesktopIntegration.open(model.getUrl())); grid.add(openInBrowser, 1, 4); GridPane.setMargin(openInBrowser, new Insets(10)); root.setCenter(grid); @@ -202,7 +203,7 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener { private void follow(Model model) { setCursor(Cursor.WAIT); - new Thread(() -> { + GlobalThreadPool.submit(() -> { try { SiteUiFactory.getUi(model.getSite()).login(); model.follow(); @@ -210,30 +211,26 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener { LOG.error("Couldn't follow model {}", model, e); showErrorDialog("Oh no!", "Couldn't follow model", e.getMessage()); } finally { - Platform.runLater(() -> { - setCursor(Cursor.DEFAULT); - }); + Platform.runLater(() -> setCursor(Cursor.DEFAULT)); } - }).start(); + }); } private void record(Model model) { setCursor(Cursor.WAIT); - new Thread(() -> { + GlobalThreadPool.submit(() -> { try { recorder.addModel(model); } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { showErrorDialog("Oh no!", "Couldn't add model to the recorder", "Recorder error: " + e.getMessage()); } finally { - Platform.runLater(() -> { - setCursor(Cursor.DEFAULT); - }); + Platform.runLater(() -> setCursor(Cursor.DEFAULT)); } - }).start(); + }); } private void loadImage(Model model, ImageView thumb) { - new Thread(() -> { + GlobalThreadPool.submit(() -> { try { String url = camsoda.getBaseUrl() + "/api/v1/user/" + model.getName(); Request detailRequest = new Request.Builder().url(url).build(); @@ -270,7 +267,7 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener { } catch (Exception e) { LOG.error("Couldn't load model details", e); } - }).start(); + }); } private Node createLabel(String string, boolean bold) { diff --git a/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java b/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java index 25a36772..20f22702 100644 --- a/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java +++ b/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java @@ -21,6 +21,7 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import ctbrec.Config; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.Model.State; import ctbrec.io.HttpException; @@ -285,7 +286,7 @@ public class ThumbCell extends StackPane { Thread.currentThread().interrupt(); return false; } - }).whenComplete((result, exception) -> { + }, GlobalThreadPool.get()).whenComplete((result, exception) -> { startPreview = null; if (result.booleanValue()) { setPreviewVisible(previewTrigger, true); @@ -587,7 +588,7 @@ public class ThumbCell extends StackPane { } finally { Platform.runLater(() -> setCursor(Cursor.DEFAULT)); } - }); + }, GlobalThreadPool.get()); } void recordLater(boolean recordLater) { diff --git a/client/src/main/java/ctbrec/ui/tabs/UpdateTab.java b/client/src/main/java/ctbrec/ui/tabs/UpdateTab.java index 7dbf38ac..1292f832 100644 --- a/client/src/main/java/ctbrec/ui/tabs/UpdateTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/UpdateTab.java @@ -1,10 +1,9 @@ package ctbrec.ui.tabs; -import java.util.concurrent.CompletableFuture; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ctbrec.GlobalThreadPool; import ctbrec.io.HttpException; import ctbrec.ui.CamrecApplication; import ctbrec.ui.CamrecApplication.Release; @@ -47,7 +46,7 @@ public class UpdateTab extends Tab implements TabSelectionListener { } public void loadChangeLog() { - CompletableFuture.runAsync(() -> { + GlobalThreadPool.submit(() -> { Request req = new Request.Builder().url("https://pastebin.com/raw/fiAPtM0s").build(); try (Response resp = CamrecApplication.httpClient.execute(req)) { if (resp.isSuccessful()) { From ec9c463f80d248fe4e2bb27617ab9b02dcd8f2af Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sun, 10 Jan 2021 20:17:18 +0100 Subject: [PATCH 04/17] Move global thread pool to common --- {client => common}/src/main/java/ctbrec/GlobalThreadPool.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {client => common}/src/main/java/ctbrec/GlobalThreadPool.java (100%) diff --git a/client/src/main/java/ctbrec/GlobalThreadPool.java b/common/src/main/java/ctbrec/GlobalThreadPool.java similarity index 100% rename from client/src/main/java/ctbrec/GlobalThreadPool.java rename to common/src/main/java/ctbrec/GlobalThreadPool.java From 967b5dec42dbfb10bcbf7c68c14c4c989fb5d2de Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sun, 10 Jan 2021 22:14:20 +0100 Subject: [PATCH 05/17] Use global thread pool instead of creating new threads --- .../main/java/ctbrec/ui/ExternalBrowser.java | 21 +++----- client/src/main/java/ctbrec/ui/Player.java | 2 - .../java/ctbrec/ui/PreviewPopupHandler.java | 17 +++--- .../ui/StreamSourceSelectionDialog.java | 12 +++-- client/src/main/java/ctbrec/ui/TipDialog.java | 27 ++++++---- .../src/main/java/ctbrec/ui/TokenLabel.java | 26 ++++++---- .../ui/action/CheckModelAccountAction.java | 3 +- .../ctbrec/ui/action/EditNotesAction.java | 35 ++++++------- .../ctbrec/ui/action/ModelMassEditAction.java | 11 +--- .../ctbrec/ui/action/OpenRecordingsDir.java | 3 +- .../ui/action/ToggleRecordingAction.java | 5 +- .../ui/controls/SearchPopoverTreeList.java | 13 ++--- .../ctbrec/ui/controls/StreamPreview.java | 7 +-- .../main/java/ctbrec/ui/controls/Toast.java | 29 ++++++----- .../src/main/java/ctbrec/ui/news/NewsTab.java | 17 +++--- .../bonga/BongaCamsElectronLoginDialog.java | 19 ++++--- .../ui/sites/bonga/BongaCamsSiteUi.java | 44 +++++----------- .../sites/cam4/Cam4ElectronLoginDialog.java | 32 +++++++----- .../java/ctbrec/ui/sites/cam4/Cam4SiteUi.java | 35 +++---------- .../ui/sites/cam4/Cam4UpdateService.java | 26 ++++------ .../chaturbate/ChaturbateUpdateService.java | 22 ++++---- .../ui/sites/fc2live/Fc2LiveSiteUi.java | 25 ++++++--- .../ui/sites/jasmin/LiveJasminSiteUi.java | 38 ++++---------- .../sites/myfreecams/MyFreeCamsTableTab.java | 10 ++-- .../ctbrec/ui/sites/showup/ShowupSiteUi.java | 33 +++--------- .../ui/sites/stripchat/StripchatSiteUi.java | 32 ++---------- .../java/ctbrec/ui/tabs/RecordingsTab.java | 26 +++++----- .../main/java/ctbrec/ui/tabs/ThumbCell.java | 13 ++--- .../java/ctbrec/ui/tabs/ThumbOverviewTab.java | 5 +- .../main/java/ctbrec/GlobalThreadPool.java | 2 +- .../download/hls/MergedFfmpegHlsDownload.java | 3 -- .../jasmin/LiveJasminTippingWebSocket.java | 5 +- .../ctbrec/recorder/server/HttpServer.java | 6 ++- .../recorder/server/RecorderServlet.java | 52 ++++++++++--------- 34 files changed, 283 insertions(+), 373 deletions(-) diff --git a/client/src/main/java/ctbrec/ui/ExternalBrowser.java b/client/src/main/java/ctbrec/ui/ExternalBrowser.java index ab1580b0..269944bd 100644 --- a/client/src/main/java/ctbrec/ui/ExternalBrowser.java +++ b/client/src/main/java/ctbrec/ui/ExternalBrowser.java @@ -28,11 +28,10 @@ public class ExternalBrowser implements AutoCloseable { private static final ExternalBrowser INSTANCE = new ExternalBrowser(); private Lock lock = new ReentrantLock(); - private Process p; private Consumer messageListener; private InputStream in; private OutputStream out; - private Socket socket; + private Socket socket; // NOSONAR private Thread reader; private volatile boolean stopped = true; private volatile boolean browserReady = false; @@ -53,10 +52,10 @@ public class ExternalBrowser implements AutoCloseable { File configDir = new File(Config.getInstance().getConfigDir(), "ctbrec-minimal-browser"); String[] cmdline = OS.getBrowserCommand(configDir.getCanonicalPath()); - p = new ProcessBuilder(cmdline).start(); + Process p = new ProcessBuilder(cmdline).start(); if (LOG.isTraceEnabled()) { - new Thread(new StreamRedirector(p.getInputStream(), System.out)).start(); - new Thread(new StreamRedirector(p.getErrorStream(), System.err)).start(); + new Thread(new StreamRedirector(p.getInputStream(), System.out)).start(); // NOSONAR + new Thread(new StreamRedirector(p.getErrorStream(), System.err)).start(); // NOSONAR } else { new Thread(new StreamRedirector(p.getInputStream(), OutputStream.nullOutputStream())).start(); new Thread(new StreamRedirector(p.getErrorStream(), OutputStream.nullOutputStream())).start(); @@ -81,7 +80,6 @@ public class ExternalBrowser implements AutoCloseable { LOG.debug("Waiting for browser to terminate"); p.waitFor(); int exitValue = p.exitValue(); - p = null; reader = null; in = null; out = null; @@ -119,7 +117,6 @@ public class ExternalBrowser implements AutoCloseable { } public void executeJavaScript(String javaScript) throws IOException { - //LOG.debug("Executing JS {}", javaScript); JSONObject script = new JSONObject(); script.put("execute", javaScript); out.write(script.toString().getBytes(UTF_8)); @@ -141,8 +138,7 @@ public class ExternalBrowser implements AutoCloseable { private void readBrowserOutput() { LOG.debug("Browser output reader started"); - try { - BufferedReader br = new BufferedReader(new InputStreamReader(in)); + try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) { String line; synchronized (browserReadyLock) { browserReady = true; @@ -150,11 +146,8 @@ public class ExternalBrowser implements AutoCloseable { } while( !Thread.interrupted() && (line = br.readLine()) != null ) { LOG.debug("Browser output: {}", line); - if(!line.startsWith("{")) { - } else { - if(messageListener != null) { - messageListener.accept(line); - } + if (line.startsWith("{") && messageListener != null) { + messageListener.accept(line); } } } catch (IOException e) { diff --git a/client/src/main/java/ctbrec/ui/Player.java b/client/src/main/java/ctbrec/ui/Player.java index d6653a9b..468ab883 100644 --- a/client/src/main/java/ctbrec/ui/Player.java +++ b/client/src/main/java/ctbrec/ui/Player.java @@ -166,12 +166,10 @@ public class Player { // create threads, which read stdout and stderr of the player process. these are needed, // because otherwise the internal buffer for these streams fill up and block the process Thread std = new Thread(new StreamRedirector(playerProcess.getInputStream(), OutputStream.nullOutputStream())); - //Thread std = new Thread(new StreamRedirectThread(playerProcess.getInputStream(), System.out)); std.setName("Player stdout pipe"); std.setDaemon(true); std.start(); Thread err = new Thread(new StreamRedirector(playerProcess.getErrorStream(), OutputStream.nullOutputStream())); - //Thread err = new Thread(new StreamRedirectThread(playerProcess.getErrorStream(), System.err)); err.setName("Player stderr pipe"); err.setDaemon(true); err.start(); diff --git a/client/src/main/java/ctbrec/ui/PreviewPopupHandler.java b/client/src/main/java/ctbrec/ui/PreviewPopupHandler.java index 5c05eb98..40cfb937 100644 --- a/client/src/main/java/ctbrec/ui/PreviewPopupHandler.java +++ b/client/src/main/java/ctbrec/ui/PreviewPopupHandler.java @@ -22,7 +22,7 @@ import javafx.scene.layout.StackPane; import javafx.stage.Popup; public class PreviewPopupHandler implements EventHandler { - private static final transient Logger LOG = LoggerFactory.getLogger(PreviewPopupHandler.class); + private static final Logger LOG = LoggerFactory.getLogger(PreviewPopupHandler.class); private static final int offset = 10; private long timeForPopupOpen = TimeUnit.SECONDS.toMillis(1); @@ -67,11 +67,11 @@ public class PreviewPopupHandler implements EventHandler { } else if(event.getEventType() == MouseEvent.MOUSE_ENTERED) { popup.setX(event.getScreenX()+ offset); popup.setY(event.getScreenY()+ offset); - JavaFxModel model = getModel(event); - if(model != null) { + JavaFxModel newModel = getModel(event); + if(newModel != null) { closeCountdown = -1; - boolean modelChanged = model != this.model; - this.model = model; + boolean modelChanged = newModel != this.model; + this.model = newModel; if(popup.isShowing()) { openCountdown = -1; if(modelChanged) { @@ -97,15 +97,15 @@ public class PreviewPopupHandler implements EventHandler { @SuppressWarnings("unchecked") TableRow row = (TableRow) event.getSource(); TableView table = row.getTableView(); - double offset = 0; + double columnOffset = 0; double width = 0; for (TableColumn col : table.getColumns()) { - offset += width; + columnOffset += width; width = col.getWidth(); if(Objects.equals(col.getId(), "preview")) { Point2D screenToLocal = table.screenToLocal(event.getScreenX(), event.getScreenY()); double x = screenToLocal.getX(); - return x >= offset && x <= offset + width; + return x >= columnOffset && x <= columnOffset + width; } } return false; @@ -176,6 +176,7 @@ public class PreviewPopupHandler implements EventHandler { try { Thread.sleep(100); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); LOG.error("PreviewPopupTimer interrupted"); break; } diff --git a/client/src/main/java/ctbrec/ui/StreamSourceSelectionDialog.java b/client/src/main/java/ctbrec/ui/StreamSourceSelectionDialog.java index 2aa64e8f..b23558fa 100644 --- a/client/src/main/java/ctbrec/ui/StreamSourceSelectionDialog.java +++ b/client/src/main/java/ctbrec/ui/StreamSourceSelectionDialog.java @@ -7,6 +7,7 @@ import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.function.Function; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.recorder.download.StreamSource; import ctbrec.ui.controls.Dialogs; @@ -19,9 +20,10 @@ import javafx.stage.Stage; public class StreamSourceSelectionDialog { private static final StreamSource BEST = new BestStreamSource(); - private StreamSourceSelectionDialog() {} + private StreamSourceSelectionDialog() { + } - public static void show(Scene parent, Model model, Function onSuccess, Function onFail) { + public static void show(Scene parent, Model model, Function onSuccess, Function onFail) { Task> selectStreamSource = new Task>() { @Override protected List call() throws Exception { @@ -35,7 +37,7 @@ public class StreamSourceSelectionDialog { List sources; try { sources = selectStreamSource.get(); - int selectedIndex = model.getStreamUrlIndex() > -1 ? Math.min(model.getStreamUrlIndex(), sources.size()-1) : sources.size()-1; + int selectedIndex = model.getStreamUrlIndex() > -1 ? Math.min(model.getStreamUrlIndex(), sources.size() - 1) : sources.size() - 1; ChoiceDialog choiceDialog = new ChoiceDialog<>(sources.get(selectedIndex), sources); choiceDialog.setTitle("Stream Quality"); choiceDialog.setHeaderText("Select your preferred stream quality"); @@ -45,7 +47,7 @@ public class StreamSourceSelectionDialog { InputStream icon = Dialogs.class.getResourceAsStream("/icon.png"); stage.getIcons().add(new Image(icon)); Optional selectedSource = choiceDialog.showAndWait(); - if(selectedSource.isPresent()) { + if (selectedSource.isPresent()) { int index = -1; if (selectedSource.get() != BEST) { index = sources.indexOf(selectedSource.get()); @@ -61,7 +63,7 @@ public class StreamSourceSelectionDialog { } }); selectStreamSource.setOnFailed(e -> onFail.apply(selectStreamSource.getException())); - new Thread(selectStreamSource).start(); + GlobalThreadPool.submit(selectStreamSource); } private static class BestStreamSource extends StreamSource { diff --git a/client/src/main/java/ctbrec/ui/TipDialog.java b/client/src/main/java/ctbrec/ui/TipDialog.java index 459d8d8a..bd2cfd00 100644 --- a/client/src/main/java/ctbrec/ui/TipDialog.java +++ b/client/src/main/java/ctbrec/ui/TipDialog.java @@ -7,7 +7,7 @@ import java.util.concurrent.ExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import ctbrec.Model; +import ctbrec.GlobalThreadPool; import ctbrec.sites.Site; import javafx.application.Platform; import javafx.concurrent.Task; @@ -19,11 +19,11 @@ import javafx.stage.Stage; public class TipDialog extends TextInputDialog { - private static final transient Logger LOG = LoggerFactory.getLogger(TipDialog.class); + private static final Logger LOG = LoggerFactory.getLogger(TipDialog.class); private Site site; private Scene parent; - public TipDialog(Scene parent, Site site, Model model) { + public TipDialog(Scene parent, Site site) { this.parent = parent; this.site = site; setTitle("Send Tip"); @@ -32,7 +32,7 @@ public class TipDialog extends TextInputDialog { setContentText("Amount of tokens to tip:"); setResizable(true); getEditor().setDisable(true); - if(parent != null) { + if (parent != null) { Stage stage = (Stage) getDialogPane().getScene().getWindow(); stage.getScene().getStylesheets().addAll(parent.getStylesheets()); } @@ -56,14 +56,14 @@ public class TipDialog extends TextInputDialog { double tokens = get(); Platform.runLater(() -> { if (tokens <= 0) { - String msg = "Do you want to buy tokens now?\n\nIf you agree, "+site.getName()+" will open in a browser. " + String msg = "Do you want to buy tokens now?\n\nIf you agree, " + site.getName() + " will open in a browser. " + "The used address is an affiliate link, which supports me, but doesn't cost you anything more."; Alert buyTokens = new AutosizeAlert(Alert.AlertType.CONFIRMATION, msg, parent, ButtonType.NO, ButtonType.YES); buyTokens.setTitle("No tokens"); buyTokens.setHeaderText("You don't have any tokens"); buyTokens.showAndWait(); TipDialog.this.close(); - if(buyTokens.getResult() == ButtonType.YES) { + if (buyTokens.getResult() == ButtonType.YES) { DesktopIntegration.open(site.getAffiliateLink()); } } else { @@ -72,13 +72,20 @@ public class TipDialog extends TextInputDialog { setHeaderText("Current token balance: " + df.format(tokens)); } }); - } catch (InterruptedException | ExecutionException e) { - LOG.error("Couldn't retrieve account balance", e); - showErrorDialog(e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + handleExcetion(e); + } catch (ExecutionException e) { + handleExcetion(e); } } }; - new Thread(task).start(); + GlobalThreadPool.submit(task); + } + + private void handleExcetion(Exception e) { + LOG.error("Couldn't retrieve account balance", e); + showErrorDialog(e); } private void showErrorDialog(Throwable throwable) { diff --git a/client/src/main/java/ctbrec/ui/TokenLabel.java b/client/src/main/java/ctbrec/ui/TokenLabel.java index 917dad12..958ef5e6 100644 --- a/client/src/main/java/ctbrec/ui/TokenLabel.java +++ b/client/src/main/java/ctbrec/ui/TokenLabel.java @@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory; import com.google.common.eventbus.Subscribe; +import ctbrec.GlobalThreadPool; import ctbrec.event.EventBusHolder; import ctbrec.sites.Site; import javafx.application.Platform; @@ -19,7 +20,7 @@ import javafx.scene.control.Tooltip; public class TokenLabel extends Label { - private static final transient Logger LOG = LoggerFactory.getLogger(TokenLabel.class); + private static final Logger LOG = LoggerFactory.getLogger(TokenLabel.class); private double tokens = -1; private Site site; @@ -72,17 +73,24 @@ public class TokenLabel extends Label { @Override protected void done() { try { - double tokens = get(); + tokens = get(); update(tokens); - } catch (InterruptedException | ExecutionException e) { - LOG.error("Couldn't retrieve account balance", e); - Platform.runLater(() -> { - setText("Tokens: error"); - setTooltip(new Tooltip(e.getMessage())); - }); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + handleException(e); + } catch (ExecutionException e) { + handleException(e); } } + + private void handleException(Exception e) { + LOG.error("Couldn't retrieve account balance", e); + Platform.runLater(() -> { + setText("Tokens: error"); + setTooltip(new Tooltip(e.getMessage())); + }); + } }; - new Thread(task).start(); + GlobalThreadPool.submit(task); } } diff --git a/client/src/main/java/ctbrec/ui/action/CheckModelAccountAction.java b/client/src/main/java/ctbrec/ui/action/CheckModelAccountAction.java index c9bab392..04fb1f94 100644 --- a/client/src/main/java/ctbrec/ui/action/CheckModelAccountAction.java +++ b/client/src/main/java/ctbrec/ui/action/CheckModelAccountAction.java @@ -9,6 +9,7 @@ import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.recorder.Recorder; import ctbrec.ui.controls.Dialogs; @@ -70,6 +71,6 @@ public class CheckModelAccountAction { }); } }); - new Thread(checker).start(); + GlobalThreadPool.submit(checker); } } diff --git a/client/src/main/java/ctbrec/ui/action/EditNotesAction.java b/client/src/main/java/ctbrec/ui/action/EditNotesAction.java index 2a23edec..0a7772c3 100644 --- a/client/src/main/java/ctbrec/ui/action/EditNotesAction.java +++ b/client/src/main/java/ctbrec/ui/action/EditNotesAction.java @@ -10,7 +10,6 @@ import ctbrec.Config; import ctbrec.Model; import ctbrec.ui.JavaFxModel; import ctbrec.ui.controls.Dialogs; -import javafx.application.Platform; import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.control.TableView; @@ -30,23 +29,21 @@ public class EditNotesAction { public void execute() { source.setCursor(Cursor.WAIT); - new Thread(() -> Platform.runLater(() -> { - String notes = Config.getInstance().getSettings().modelNotes.getOrDefault(model.getUrl(), ""); - Optional newNotes = Dialogs.showTextInput(source.getScene(), "Model Notes", "Notes for " + model.getName(), notes); - newNotes.ifPresent(n -> { - if (!n.trim().isEmpty()) { - Config.getInstance().getSettings().modelNotes.put(model.getUrl(), n); - } else { - Config.getInstance().getSettings().modelNotes.remove(model.getUrl()); - } - try { - Config.getInstance().save(); - } catch (IOException e) { - LOG.warn("Couldn't save config", e); - } - }); - table.refresh(); - source.setCursor(Cursor.DEFAULT); - })).start(); + String notes = Config.getInstance().getSettings().modelNotes.getOrDefault(model.getUrl(), ""); + Optional newNotes = Dialogs.showTextInput(source.getScene(), "Model Notes", "Notes for " + model.getName(), notes); + newNotes.ifPresent(n -> { + if (!n.trim().isEmpty()) { + Config.getInstance().getSettings().modelNotes.put(model.getUrl(), n); + } else { + Config.getInstance().getSettings().modelNotes.remove(model.getUrl()); + } + try { + Config.getInstance().save(); + } catch (IOException e) { + LOG.warn("Couldn't save config", e); + } + }); + table.refresh(); + source.setCursor(Cursor.DEFAULT); } } diff --git a/client/src/main/java/ctbrec/ui/action/ModelMassEditAction.java b/client/src/main/java/ctbrec/ui/action/ModelMassEditAction.java index 70c6dfc1..d3304153 100644 --- a/client/src/main/java/ctbrec/ui/action/ModelMassEditAction.java +++ b/client/src/main/java/ctbrec/ui/action/ModelMassEditAction.java @@ -2,13 +2,9 @@ package ctbrec.ui.action; import java.util.List; import java.util.Objects; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import javafx.application.Platform; import javafx.scene.Cursor; @@ -16,9 +12,6 @@ import javafx.scene.Node; public class ModelMassEditAction { - static BlockingQueue queue = new LinkedBlockingQueue<>(); - static ExecutorService threadPool = new ThreadPoolExecutor(2, 10, 10, TimeUnit.MINUTES, queue); - protected List models; protected Consumer action; protected Node source; @@ -42,7 +35,7 @@ public class ModelMassEditAction { Consumer cb = Objects.requireNonNull(callback, "Callback is null, call execute() instead"); source.setCursor(Cursor.WAIT); for (Model model : models) { - threadPool.submit(() -> { + GlobalThreadPool.submit(() -> { action.accept(model); cb.accept(model); Platform.runLater(() -> source.setCursor(Cursor.DEFAULT)); diff --git a/client/src/main/java/ctbrec/ui/action/OpenRecordingsDir.java b/client/src/main/java/ctbrec/ui/action/OpenRecordingsDir.java index 4ef67fd0..10d779b1 100644 --- a/client/src/main/java/ctbrec/ui/action/OpenRecordingsDir.java +++ b/client/src/main/java/ctbrec/ui/action/OpenRecordingsDir.java @@ -4,6 +4,7 @@ import java.io.File; import java.time.Instant; import ctbrec.Config; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.Settings.DirectoryStructure; import ctbrec.ui.DesktopIntegration; @@ -26,7 +27,7 @@ public class OpenRecordingsDir { File fileForRecording = Config.getInstance().getFileForRecording(selectedModel, ".mp4", Instant.now()); final File dir = getModelDirectory(fileForRecording); if (dir.exists()) { - new Thread(() -> DesktopIntegration.open(dir)).start(); + GlobalThreadPool.submit(() -> DesktopIntegration.open(dir)); } else { Dialogs.showError(source.getScene(), "Directory does not exist", "There are no recordings for this model", null); } diff --git a/client/src/main/java/ctbrec/ui/action/ToggleRecordingAction.java b/client/src/main/java/ctbrec/ui/action/ToggleRecordingAction.java index 6e9fc155..0e550f50 100644 --- a/client/src/main/java/ctbrec/ui/action/ToggleRecordingAction.java +++ b/client/src/main/java/ctbrec/ui/action/ToggleRecordingAction.java @@ -1,5 +1,6 @@ package ctbrec.ui.action; +import ctbrec.GlobalThreadPool; import ctbrec.recorder.Recorder; import ctbrec.ui.controls.Dialogs; import javafx.application.Platform; @@ -20,7 +21,7 @@ public class ToggleRecordingAction { public void execute() { toggleButton.setCursor(Cursor.WAIT); - Thread t = new Thread(() -> { + GlobalThreadPool.submit(() -> { try { if (pause) { recorder.pause(); @@ -36,7 +37,5 @@ public class ToggleRecordingAction { Platform.runLater(() -> toggleButton.setCursor(Cursor.DEFAULT)); } }); - t.setDaemon(true); - t.start(); } } diff --git a/client/src/main/java/ctbrec/ui/controls/SearchPopoverTreeList.java b/client/src/main/java/ctbrec/ui/controls/SearchPopoverTreeList.java index 966a2b21..04fadda5 100644 --- a/client/src/main/java/ctbrec/ui/controls/SearchPopoverTreeList.java +++ b/client/src/main/java/ctbrec/ui/controls/SearchPopoverTreeList.java @@ -38,6 +38,7 @@ import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.recorder.Recorder; import ctbrec.ui.action.PlayAction; @@ -167,7 +168,7 @@ public class SearchPopoverTreeList extends PopoverTreeList implements Pop follow = new Button("Follow"); follow.setOnAction(evt -> { setCursor(Cursor.WAIT); - new Thread(new Task() { + GlobalThreadPool.submit(new Task() { @Override protected Boolean call() throws Exception { model.getSite().login(); @@ -183,12 +184,12 @@ public class SearchPopoverTreeList extends PopoverTreeList implements Pop } Platform.runLater(() -> setCursor(Cursor.DEFAULT)); } - }).start(); + }); }); record = new Button("Record"); record.setOnAction(evt -> { setCursor(Cursor.WAIT); - new Thread(new Task() { + GlobalThreadPool.submit(new Task() { @Override protected Void call() throws Exception { recorder.addModel(model); @@ -199,7 +200,7 @@ public class SearchPopoverTreeList extends PopoverTreeList implements Pop protected void done() { Platform.runLater(() -> setCursor(Cursor.DEFAULT)); } - }).start(); + }); }); getChildren().addAll(thumb, title, follow, record); @@ -296,12 +297,12 @@ public class SearchPopoverTreeList extends PopoverTreeList implements Pop @Override protected double computePrefHeight(double width) { - return thumbSize + 20; + return thumbSize + 20.0; } @Override protected double computeMaxHeight(double width) { - return thumbSize + 20; + return thumbSize + 20.0; } @Override diff --git a/client/src/main/java/ctbrec/ui/controls/StreamPreview.java b/client/src/main/java/ctbrec/ui/controls/StreamPreview.java index 9b1eb20e..66c21239 100644 --- a/client/src/main/java/ctbrec/ui/controls/StreamPreview.java +++ b/client/src/main/java/ctbrec/ui/controls/StreamPreview.java @@ -12,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Config; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.io.HttpException; import ctbrec.recorder.download.StreamSource; @@ -79,7 +80,7 @@ public class StreamPreview extends StackPane { double w = Config.getInstance().getSettings().thumbWidth; double h = w / aspect; resizeTo(w, h); - } catch (Exception e) {} + } catch (Exception e) { /* nothing to do */ } } if(future != null && !future.isDone()) { @@ -157,14 +158,14 @@ public class StreamPreview extends StackPane { running = false; MediaPlayer old = videoPlayer; Future oldFuture = future; - new Thread(() -> { + GlobalThreadPool.submit(() -> { if(oldFuture != null && !oldFuture.isDone()) { oldFuture.cancel(true); } if(old != null) { old.dispose(); } - }).start(); + }); } private void onError(MediaPlayer videoPlayer) { diff --git a/client/src/main/java/ctbrec/ui/controls/Toast.java b/client/src/main/java/ctbrec/ui/controls/Toast.java index e622013d..28e3953c 100644 --- a/client/src/main/java/ctbrec/ui/controls/Toast.java +++ b/client/src/main/java/ctbrec/ui/controls/Toast.java @@ -1,5 +1,6 @@ package ctbrec.ui.controls; +import ctbrec.GlobalThreadPool; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; @@ -13,6 +14,8 @@ import javafx.stage.StageStyle; import javafx.util.Duration; public final class Toast { + private Toast() {} + public static void makeText(Scene owner, String toastMsg, int toastDelay, int fadeInDelay, int fadeOutDelay) { Stage toastStage = new Stage(); toastStage.initOwner(owner.getWindow()); @@ -35,20 +38,18 @@ public final class Toast { Timeline fadeInTimeline = new Timeline(); KeyFrame fadeInKey1 = new KeyFrame(Duration.millis(fadeInDelay), new KeyValue(toastStage.getScene().getRoot().opacityProperty(), 1)); fadeInTimeline.getKeyFrames().add(fadeInKey1); - fadeInTimeline.setOnFinished((ae) -> { - new Thread(() -> { - try { - Thread.sleep(toastDelay); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - Timeline fadeOutTimeline = new Timeline(); - KeyFrame fadeOutKey1 = new KeyFrame(Duration.millis(fadeOutDelay), new KeyValue(toastStage.getScene().getRoot().opacityProperty(), 0)); - fadeOutTimeline.getKeyFrames().add(fadeOutKey1); - fadeOutTimeline.setOnFinished((aeb) -> toastStage.close()); - fadeOutTimeline.play(); - }).start(); - }); + fadeInTimeline.setOnFinished(ae -> GlobalThreadPool.submit(() -> { + try { + Thread.sleep(toastDelay); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + Timeline fadeOutTimeline = new Timeline(); + KeyFrame fadeOutKey1 = new KeyFrame(Duration.millis(fadeOutDelay), new KeyValue(toastStage.getScene().getRoot().opacityProperty(), 0)); + fadeOutTimeline.getKeyFrames().add(fadeOutKey1); + fadeOutTimeline.setOnFinished(aeb -> toastStage.close()); + fadeOutTimeline.play(); + })); fadeInTimeline.play(); } } \ No newline at end of file diff --git a/client/src/main/java/ctbrec/ui/news/NewsTab.java b/client/src/main/java/ctbrec/ui/news/NewsTab.java index a5f11d8e..07861344 100644 --- a/client/src/main/java/ctbrec/ui/news/NewsTab.java +++ b/client/src/main/java/ctbrec/ui/news/NewsTab.java @@ -1,7 +1,16 @@ package ctbrec.ui.news; +import static ctbrec.io.HttpConstants.*; + +import java.io.IOException; +import java.util.Objects; + +import org.json.JSONObject; + import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.Moshi; + +import ctbrec.GlobalThreadPool; import ctbrec.io.HttpException; import ctbrec.ui.CamrecApplication; import ctbrec.ui.controls.Dialogs; @@ -14,12 +23,6 @@ import javafx.scene.control.Tab; import javafx.scene.layout.VBox; import okhttp3.Request; import okhttp3.Response; -import org.json.JSONObject; - -import java.io.IOException; -import java.util.Objects; - -import static ctbrec.io.HttpConstants.USER_AGENT; public class NewsTab extends Tab implements TabSelectionListener { private static final String ACCESS_TOKEN = "a2804d73a89951a22e0f8483a6fcec8943afd88b7ba17c459c095aa9e6f94fd0"; @@ -36,7 +39,7 @@ public class NewsTab extends Tab implements TabSelectionListener { @Override public void selected() { - new Thread(this::loadToots).start(); + GlobalThreadPool.submit(this::loadToots); } private void loadToots() { diff --git a/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsElectronLoginDialog.java b/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsElectronLoginDialog.java index 40bff63c..9aa7e908 100644 --- a/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsElectronLoginDialog.java +++ b/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsElectronLoginDialog.java @@ -22,7 +22,7 @@ import okhttp3.HttpUrl; public class BongaCamsElectronLoginDialog { - private static final transient Logger LOG = LoggerFactory.getLogger(BongaCamsElectronLoginDialog.class); + private static final Logger LOG = LoggerFactory.getLogger(BongaCamsElectronLoginDialog.class); public static final String DOMAIN = "bongacams.com"; public static final String URL = BongaCams.baseUrl + "/login"; private CookieJar cookieJar; @@ -40,18 +40,18 @@ public class BongaCamsElectronLoginDialog { msg.put("config", config); browser.run(msg, msgHandler); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); throw new IOException("Couldn't wait for login dialog", e); } finally { browser.close(); } } - private Consumer msgHandler = (line) -> { + private Consumer msgHandler = line -> { if(!line.startsWith("{")) { - System.err.println(line); + LOG.error("Didn't received a JSON object {}", line); } else { JSONObject json = new JSONObject(line); - //LOG.debug("Browser: {}", json.toString(2)); if(json.has("url")) { String url = json.getString("url"); if(url.endsWith("/login")) { @@ -82,16 +82,16 @@ public class BongaCamsElectronLoginDialog { } if(json.has("cookies")) { - JSONArray _cookies = json.getJSONArray("cookies"); - for (int i = 0; i < _cookies.length(); i++) { - JSONObject cookie = _cookies.getJSONObject(i); + JSONArray cookiesFromBrowser = json.getJSONArray("cookies"); + for (int i = 0; i < cookiesFromBrowser.length(); i++) { + JSONObject cookie = cookiesFromBrowser.getJSONObject(i); if(cookie.getString("domain").contains(DOMAIN)) { Builder b = new Cookie.Builder() .path(cookie.getString("path")) .domain(DOMAIN) .name(cookie.getString("name")) .value(cookie.getString("value")) - .expiresAt(Double.valueOf(cookie.optDouble("expirationDate")).longValue()); + .expiresAt((long) cookie.optDouble("expirationDate")); if(cookie.optBoolean("hostOnly")) { b.hostOnlyDomain(DOMAIN); } @@ -108,8 +108,7 @@ public class BongaCamsElectronLoginDialog { } try { - URL _url = new URL(url); - if (Objects.equals(_url.getPath(), "/")) { + if (Objects.equals(new URL(url).getPath(), "/")) { browser.close(); } } catch (MalformedURLException e) { diff --git a/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsSiteUi.java b/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsSiteUi.java index a75ab316..4d8e50a2 100644 --- a/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsSiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsSiteUi.java @@ -1,17 +1,16 @@ package ctbrec.ui.sites.bonga; +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import ctbrec.sites.bonga.BongaCams; import ctbrec.sites.bonga.BongaCamsHttpClient; import ctbrec.ui.controls.Dialogs; import ctbrec.ui.sites.AbstractSiteUi; import ctbrec.ui.sites.ConfigUI; import ctbrec.ui.tabs.TabProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; public class BongaCamsSiteUi extends AbstractSiteUi { @@ -39,37 +38,20 @@ public class BongaCamsSiteUi extends AbstractSiteUi { @Override public synchronized boolean login() throws IOException { boolean automaticLogin = bongaCams.login(); - if(automaticLogin) { + if (automaticLogin) { return true; } else { - BlockingQueue queue = new LinkedBlockingQueue<>(); + // login with external browser window try { - new Thread(() -> { - // login with external browser window - try { - new BongaCamsElectronLoginDialog(bongaCams.getHttpClient().getCookieJar()); - } catch (Exception e1) { - LOG.error("Error logging in with external browser", e1); - Dialogs.showError("Login error", "Couldn't login to " + bongaCams.getName(), e1); - } - - try { - queue.put(true); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - LOG.error("Error while signaling termination", e); - } - }).start(); - queue.take(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - LOG.error("Error while waiting for login dialog to close", e); - throw new IOException(e); + new BongaCamsElectronLoginDialog(bongaCams.getHttpClient().getCookieJar()); + } catch (Exception e1) { + LOG.error("Error logging in with external browser", e1); + Dialogs.showError("Login error", "Couldn't login to " + bongaCams.getName(), e1); } - BongaCamsHttpClient httpClient = (BongaCamsHttpClient)bongaCams.getHttpClient(); + BongaCamsHttpClient httpClient = (BongaCamsHttpClient) bongaCams.getHttpClient(); boolean loggedIn = httpClient.checkLoginSuccess(); - if(loggedIn) { + if (loggedIn) { LOG.info("Logged in. User ID is {}", httpClient.getUserId()); } else { LOG.info("Login failed"); diff --git a/client/src/main/java/ctbrec/ui/sites/cam4/Cam4ElectronLoginDialog.java b/client/src/main/java/ctbrec/ui/sites/cam4/Cam4ElectronLoginDialog.java index 7f79ea0a..9509138c 100644 --- a/client/src/main/java/ctbrec/ui/sites/cam4/Cam4ElectronLoginDialog.java +++ b/client/src/main/java/ctbrec/ui/sites/cam4/Cam4ElectronLoginDialog.java @@ -22,7 +22,7 @@ import okhttp3.HttpUrl; public class Cam4ElectronLoginDialog { - private static final transient Logger LOG = LoggerFactory.getLogger(Cam4ElectronLoginDialog.class); + private static final Logger LOG = LoggerFactory.getLogger(Cam4ElectronLoginDialog.class); public static final String DOMAIN = "cam4.com"; public static final String URL = Cam4.BASE_URI + "/login"; private CookieJar cookieJar; @@ -40,15 +40,16 @@ public class Cam4ElectronLoginDialog { msg.put("config", config); browser.run(msg, msgHandler); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); throw new IOException("Couldn't wait for login dialog", e); } finally { browser.close(); } } - private Consumer msgHandler = (line) -> { + private Consumer msgHandler = line -> { if(!line.startsWith("{")) { - System.err.println(line); + LOG.error("Didn't received a JSON object {}", line); } else { JSONObject json = new JSONObject(line); if(json.has("url")) { @@ -75,11 +76,10 @@ public class Cam4ElectronLoginDialog { } if(json.has("cookies")) { - JSONArray _cookies = json.getJSONArray("cookies"); + JSONArray cookiesFromBrowser = json.getJSONArray("cookies"); try { - URL _url = new URL(url); - for (int i = 0; i < _cookies.length(); i++) { - JSONObject cookie = _cookies.getJSONObject(i); + for (int i = 0; i < cookiesFromBrowser.length(); i++) { + JSONObject cookie = cookiesFromBrowser.getJSONObject(i); if(cookie.getString("domain").contains("cam4")) { String domain = cookie.getString("domain"); if(domain.startsWith(".")) { @@ -91,12 +91,8 @@ public class Cam4ElectronLoginDialog { cookieJar.saveFromResponse(HttpUrl.parse(Cam4.BASE_URI), Collections.singletonList(c)); } } - if (Objects.equals(_url.getPath(), "/")) { - try { - browser.close(); - } catch(IOException e) { - LOG.error("Couldn't send close request to browser", e); - } + if (Objects.equals(new URL(url).getPath(), "/")) { + closeBrowser(); } } catch (MalformedURLException e) { LOG.error("Couldn't parse new url {}", url, e); @@ -112,7 +108,7 @@ public class Cam4ElectronLoginDialog { .domain(domain) .name(cookie.getString("name")) .value(cookie.getString("value")) - .expiresAt(Double.valueOf(cookie.optDouble("expirationDate")).longValue()); + .expiresAt((long) cookie.optDouble("expirationDate")); if(cookie.optBoolean("hostOnly")) { b.hostOnlyDomain(domain); } @@ -124,4 +120,12 @@ public class Cam4ElectronLoginDialog { } return b.build(); } + + private void closeBrowser() { + try { + browser.close(); + } catch(IOException e) { + LOG.error("Couldn't send close request to browser", e); + } + } } diff --git a/client/src/main/java/ctbrec/ui/sites/cam4/Cam4SiteUi.java b/client/src/main/java/ctbrec/ui/sites/cam4/Cam4SiteUi.java index ad5ebdc9..3d972ba2 100644 --- a/client/src/main/java/ctbrec/ui/sites/cam4/Cam4SiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/cam4/Cam4SiteUi.java @@ -1,8 +1,6 @@ package ctbrec.ui.sites.cam4; import java.io.IOException; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,10 +11,9 @@ import ctbrec.ui.controls.Dialogs; import ctbrec.ui.sites.AbstractSiteUi; import ctbrec.ui.sites.ConfigUI; import ctbrec.ui.tabs.TabProvider; -import javafx.application.Platform; public class Cam4SiteUi extends AbstractSiteUi { - private static final transient Logger LOG = LoggerFactory.getLogger(Cam4SiteUi.class); + private static final Logger LOG = LoggerFactory.getLogger(Cam4SiteUi.class); private Cam4TabProvider tabProvider; private Cam4ConfigUI configUI; @@ -44,33 +41,13 @@ public class Cam4SiteUi extends AbstractSiteUi { if (automaticLogin) { return true; } else { - - BlockingQueue queue = new LinkedBlockingQueue<>(); - - Runnable showDialog = () -> { - // login with external browser - try { - new Cam4ElectronLoginDialog(cam4.getHttpClient().getCookieJar()); - } catch (Exception e1) { - LOG.error("Error logging in with external browser", e1); - Dialogs.showError("Login error", "Couldn't login to " + cam4.getName(), e1); - } - - try { - queue.put(true); - } catch (InterruptedException e) { - LOG.error("Error while signaling termination", e); - } - }; - - Platform.runLater(showDialog); + // login with external browser try { - queue.take(); - } catch (InterruptedException e) { - LOG.error("Error while waiting for login dialog to close", e); - throw new IOException(e); + new Cam4ElectronLoginDialog(cam4.getHttpClient().getCookieJar()); + } catch (Exception e1) { + LOG.error("Error logging in with external browser", e1); + Dialogs.showError("Login error", "Couldn't login to " + cam4.getName(), e1); } - Cam4HttpClient httpClient = (Cam4HttpClient) cam4.getHttpClient(); boolean loggedIn = httpClient.checkLoginSuccess(); return loggedIn; diff --git a/client/src/main/java/ctbrec/ui/sites/cam4/Cam4UpdateService.java b/client/src/main/java/ctbrec/ui/sites/cam4/Cam4UpdateService.java index f0537bb2..dc00deec 100644 --- a/client/src/main/java/ctbrec/ui/sites/cam4/Cam4UpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/cam4/Cam4UpdateService.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import org.json.JSONObject; import org.jsoup.nodes.Element; @@ -43,14 +42,11 @@ public class Cam4UpdateService extends PaginatedScheduledService { this.url = url; this.loginRequired = loginRequired; - ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setDaemon(true); - t.setName("ThumbOverviewTab UpdateService"); - return t; - } + ExecutorService executor = Executors.newSingleThreadExecutor(r -> { + Thread t = new Thread(r); + t.setDaemon(true); + t.setName("ThumbOverviewTab UpdateService"); + return t; }); setExecutor(executor); } @@ -60,16 +56,16 @@ public class Cam4UpdateService extends PaginatedScheduledService { return new Task>() { @Override public List call() throws IOException { - if(loginRequired && StringUtil.isBlank(Config.getInstance().getSettings().cam4Username)) { + if (loginRequired && StringUtil.isBlank(Config.getInstance().getSettings().cam4Username)) { return Collections.emptyList(); } else { - String url = Cam4UpdateService.this.url + "&page=" + page; - LOG.debug("Fetching page {}", url); - if(loginRequired) { + String pageUrl = Cam4UpdateService.this.url + "&page=" + page; + LOG.debug("Fetching page {}", pageUrl); + if (loginRequired) { SiteUiFactory.getUi(site).login(); } Request request = new Request.Builder() - .url(url) + .url(pageUrl) .header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage()) .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) .build(); @@ -91,7 +87,7 @@ public class Cam4UpdateService extends PaginatedScheduledService { model.setPreview("https://snapshots.xcdnpro.com/thumbnails/" + model.getName() + "?s=" + System.currentTimeMillis()); model.setDescription(parseDesription(boxHtml)); model.setOnlineState(ONLINE); - if(boxHtml.contains("In private show")) { + if (boxHtml.contains("In private show")) { model.setOnlineState(PRIVATE); } models.add(model); diff --git a/client/src/main/java/ctbrec/ui/sites/chaturbate/ChaturbateUpdateService.java b/client/src/main/java/ctbrec/ui/sites/chaturbate/ChaturbateUpdateService.java index 5fbcd63f..4e274d7b 100644 --- a/client/src/main/java/ctbrec/ui/sites/chaturbate/ChaturbateUpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/chaturbate/ChaturbateUpdateService.java @@ -5,7 +5,6 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,7 +20,7 @@ import okhttp3.Response; public class ChaturbateUpdateService extends PaginatedScheduledService { - private static final transient Logger LOG = LoggerFactory.getLogger(ChaturbateUpdateService.class); + private static final Logger LOG = LoggerFactory.getLogger(ChaturbateUpdateService.class); private String url; private boolean loginRequired; private Chaturbate chaturbate; @@ -31,14 +30,11 @@ public class ChaturbateUpdateService extends PaginatedScheduledService { this.loginRequired = loginRequired; this.chaturbate = chaturbate; - ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setDaemon(true); - t.setName("ThumbOverviewTab UpdateService"); - return t; - } + ExecutorService executor = Executors.newSingleThreadExecutor(r -> { + Thread t = new Thread(r); + t.setDaemon(true); + t.setName("ThumbOverviewTab UpdateService"); + return t; }); setExecutor(executor); } @@ -51,12 +47,12 @@ public class ChaturbateUpdateService extends PaginatedScheduledService { if(loginRequired && !chaturbate.credentialsAvailable()) { return Collections.emptyList(); } else { - String url = ChaturbateUpdateService.this.url + "?page="+page+"&keywords=&_=" + System.currentTimeMillis(); - LOG.debug("Fetching page {}", url); + String pageUrl = ChaturbateUpdateService.this.url + "?page="+page+"&keywords=&_=" + System.currentTimeMillis(); + LOG.debug("Fetching page {}", pageUrl); if(loginRequired) { SiteUiFactory.getUi(chaturbate).login(); } - Request request = new Request.Builder().url(url).build(); + Request request = new Request.Builder().url(pageUrl).build(); Response response = chaturbate.getHttpClient().execute(request); if (response.isSuccessful()) { List models = ChaturbateModelParser.parseModels(chaturbate, response.body().string()); diff --git a/client/src/main/java/ctbrec/ui/sites/fc2live/Fc2LiveSiteUi.java b/client/src/main/java/ctbrec/ui/sites/fc2live/Fc2LiveSiteUi.java index 2e160aae..2b722627 100644 --- a/client/src/main/java/ctbrec/ui/sites/fc2live/Fc2LiveSiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/fc2live/Fc2LiveSiteUi.java @@ -5,6 +5,7 @@ import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.sites.fc2live.Fc2Live; import ctbrec.sites.fc2live.Fc2Model; @@ -16,7 +17,7 @@ import ctbrec.ui.sites.ConfigUI; import ctbrec.ui.tabs.TabProvider; public class Fc2LiveSiteUi extends AbstractSiteUi { - private static final transient Logger LOG = LoggerFactory.getLogger(Fc2LiveSiteUi.class); + private static final Logger LOG = LoggerFactory.getLogger(Fc2LiveSiteUi.class); private Fc2Live fc2live; private Fc2TabProvider tabProvider; private Fc2LiveConfigUI configUi; @@ -44,10 +45,10 @@ public class Fc2LiveSiteUi extends AbstractSiteUi { @Override public boolean play(Model model) { - new Thread(() -> { + GlobalThreadPool.submit(() -> { Fc2Model m; - if(model instanceof JavaFxModel) { - m = (Fc2Model) ((JavaFxModel)model).getDelegate(); + if (model instanceof JavaFxModel) { + m = (Fc2Model) ((JavaFxModel) model).getDelegate(); } else { m = (Fc2Model) model; } @@ -55,12 +56,20 @@ public class Fc2LiveSiteUi extends AbstractSiteUi { m.openWebsocket(); LOG.debug("Starting player for {}", model); Player.play(model, false); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + handleException(e); + } catch (IOException e) { + handleException(e); + } finally { m.closeWebsocket(); - } catch (InterruptedException | IOException e) { - LOG.error("Error playing the stream", e); - Dialogs.showError("Player", "Error playing the stream", e); } - }).start(); + }); return true; } + + private void handleException(Exception e) { + LOG.error("Error playing the stream", e); + Dialogs.showError("Player", "Error playing the stream", e); + } } diff --git a/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminSiteUi.java b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminSiteUi.java index 5975a489..f4ddbba7 100644 --- a/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminSiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminSiteUi.java @@ -1,8 +1,6 @@ package ctbrec.ui.sites.jasmin; import java.io.IOException; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -17,7 +15,7 @@ import ctbrec.ui.tabs.TabProvider; public class LiveJasminSiteUi extends AbstractSiteUi { - private static final transient Logger LOG = LoggerFactory.getLogger(LiveJasminSiteUi.class); + private static final Logger LOG = LoggerFactory.getLogger(LiveJasminSiteUi.class); private LiveJasmin liveJasmin; private LiveJasminTabProvider tabProvider; private LiveJasminConfigUi configUi; @@ -44,43 +42,27 @@ public class LiveJasminSiteUi extends AbstractSiteUi { // renew login every 30 min long now = System.currentTimeMillis(); boolean renew = false; - if((now - lastLoginTime) > TimeUnit.MINUTES.toMillis(30)) { + if ((now - lastLoginTime) > TimeUnit.MINUTES.toMillis(30)) { renew = true; } boolean automaticLogin = liveJasmin.login(); - if(automaticLogin && !renew) { + if (automaticLogin && !renew) { return true; } else { lastLoginTime = System.currentTimeMillis(); - BlockingQueue queue = new LinkedBlockingQueue<>(); - - new Thread (() -> { - // login with external browser window - try { - new LiveJasminElectronLoginDialog(liveJasmin.getHttpClient().getCookieJar()); - } catch (Exception e1) { - LOG.error("Error logging in with external browser", e1); - Dialogs.showError("Login error", "Couldn't login to " + liveJasmin.getName(), e1); - } - - try { - queue.put(true); - } catch (InterruptedException e) { - LOG.error("Error while signaling termination", e); - } - }).start(); + // login with external browser window try { - queue.take(); - } catch (InterruptedException e) { - LOG.error("Error while waiting for login dialog to close", e); - throw new IOException(e); + new LiveJasminElectronLoginDialog(liveJasmin.getHttpClient().getCookieJar()); + } catch (Exception e1) { + LOG.error("Error logging in with external browser", e1); + Dialogs.showError("Login error", "Couldn't login to " + liveJasmin.getName(), e1); } - LiveJasminHttpClient httpClient = (LiveJasminHttpClient)liveJasmin.getHttpClient(); + LiveJasminHttpClient httpClient = (LiveJasminHttpClient) liveJasmin.getHttpClient(); boolean loggedIn = httpClient.checkLoginSuccess(); - if(loggedIn) { + if (loggedIn) { LOG.info("Logged in"); } else { LOG.info("Login failed"); diff --git a/client/src/main/java/ctbrec/ui/sites/myfreecams/MyFreeCamsTableTab.java b/client/src/main/java/ctbrec/ui/sites/myfreecams/MyFreeCamsTableTab.java index c09865fb..10f56003 100644 --- a/client/src/main/java/ctbrec/ui/sites/myfreecams/MyFreeCamsTableTab.java +++ b/client/src/main/java/ctbrec/ui/sites/myfreecams/MyFreeCamsTableTab.java @@ -33,6 +33,7 @@ import com.iheartradio.m3u8.ParseException; import com.iheartradio.m3u8.PlaylistException; import ctbrec.Config; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.Settings; import ctbrec.StringUtil; @@ -353,7 +354,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener { if (Objects.equals(System.getenv("CTBREC_DEV"), "1")) { MenuItem debug = new MenuItem("debug"); - debug.setOnAction(e -> new Thread(() -> { + debug.setOnAction(e -> GlobalThreadPool.submit(() -> { for (Model m : selectedModels) { try { List sources = m.getStreamSources(); @@ -365,7 +366,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener { LOG.error("Couldn't get stream sources", e1); } } - }).start()); + })); menu.getItems().add(debug); } @@ -464,7 +465,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener { private String escape(Property prop) { String value = prop.getValue() != null ? prop.getValue().toString() : ""; - return "\"" + value.replaceAll("\"", "\"\"") + "\""; + return "\"" + value.replace("\"", "\"\"") + "\""; } private void showColumnSelection(ActionEvent evt) { @@ -566,7 +567,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener { if(!file.exists()) { return; } - String json = new String(Files.readAllBytes(file.toPath()), "utf-8"); + String json = new String(Files.readAllBytes(file.toPath()), UTF_8); JSONArray data = new JSONArray(json); for (int i = 0; i < data.length(); i++) { try { @@ -701,7 +702,6 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener { setProperty(continent, Optional.ofNullable(st.getM()).map(ctbrec.sites.mfc.Model::getContinent)); setProperty(occupation, Optional.ofNullable(st.getU()).map(User::getOccupation)); int flags = Optional.ofNullable(st.getM()).map(ctbrec.sites.mfc.Model::getFlags).orElse(0); - //isHd.set((flags & 1024) == 1024); isWebrtc.set((flags & 524288) == 524288); isHd.set(Optional.ofNullable(st.getU()).map(User::getPhase).orElse("z").equalsIgnoreCase("a")); flagsProperty.setValue(flags); diff --git a/client/src/main/java/ctbrec/ui/sites/showup/ShowupSiteUi.java b/client/src/main/java/ctbrec/ui/sites/showup/ShowupSiteUi.java index 3ab05d27..97cc1eb1 100644 --- a/client/src/main/java/ctbrec/ui/sites/showup/ShowupSiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/showup/ShowupSiteUi.java @@ -1,8 +1,6 @@ package ctbrec.ui.sites.showup; import java.io.IOException; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,33 +42,17 @@ public class ShowupSiteUi extends AbstractSiteUi { if (automaticLogin) { return true; } else { - BlockingQueue queue = new LinkedBlockingQueue<>(); + // login with external browser window try { - new Thread(() -> { - // login with external browser window - try { - new ShowupElectronLoginDialog(site.getHttpClient().getCookieJar()); - } catch (Exception e1) { - LOG.error("Error logging in with external browser", e1); - Dialogs.showError("Login error", "Couldn't login to " + site.getName(), e1); - } - - try { - queue.put(true); - } catch (InterruptedException e) { - LOG.error("Error while signaling termination", e); - Thread.currentThread().interrupt(); - } - }).start(); - queue.take(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IOException(e); + new ShowupElectronLoginDialog(site.getHttpClient().getCookieJar()); + } catch (Exception e1) { + LOG.error("Error logging in with external browser", e1); + Dialogs.showError("Login error", "Couldn't login to " + site.getName(), e1); } - ShowupHttpClient httpClient = (ShowupHttpClient)site.getHttpClient(); + ShowupHttpClient httpClient = (ShowupHttpClient) site.getHttpClient(); boolean loggedIn = httpClient.checkLoginSuccess(); - if(loggedIn) { + if (loggedIn) { LOG.info("Logged in"); } else { LOG.info("Login failed"); @@ -78,5 +60,4 @@ public class ShowupSiteUi extends AbstractSiteUi { return loggedIn; } } - } diff --git a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatSiteUi.java b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatSiteUi.java index af898505..9ecdb584 100644 --- a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatSiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatSiteUi.java @@ -1,8 +1,6 @@ package ctbrec.ui.sites.stripchat; import java.io.IOException; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,7 +11,6 @@ import ctbrec.ui.controls.Dialogs; import ctbrec.ui.sites.AbstractSiteUi; import ctbrec.ui.sites.ConfigUI; import ctbrec.ui.tabs.TabProvider; -import javafx.application.Platform; public class StripchatSiteUi extends AbstractSiteUi { @@ -45,31 +42,12 @@ public class StripchatSiteUi extends AbstractSiteUi { if (automaticLogin) { return true; } else { - - BlockingQueue queue = new LinkedBlockingQueue<>(); - - Runnable showDialog = () -> { - // login with external browser - try { - new StripchatElectronLoginDialog(site.getHttpClient().getCookieJar()); - } catch (Exception e1) { - LOG.error("Error logging in with external browser", e1); - Dialogs.showError("Login error", "Couldn't login to " + site.getName(), e1); - } - - try { - queue.put(true); - } catch (InterruptedException e) { - LOG.error("Error while signaling termination", e); - } - }; - - Platform.runLater(showDialog); + // login with external browser try { - queue.take(); - } catch (InterruptedException e) { - LOG.error("Error while waiting for login dialog to close", e); - throw new IOException(e); + new StripchatElectronLoginDialog(site.getHttpClient().getCookieJar()); + } catch (Exception e1) { + LOG.error("Error logging in with external browser", e1); + Dialogs.showError("Login error", "Couldn't login to " + site.getName(), e1); } StripchatHttpClient httpClient = (StripchatHttpClient) site.getHttpClient(); diff --git a/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java b/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java index beba4d80..d7e51802 100644 --- a/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java @@ -28,6 +28,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Config; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.Recording; import ctbrec.Recording.State; @@ -496,9 +497,9 @@ public class RecordingsTab extends Tab implements TabSelectionListener { private void openContactSheet(JavaFxRecording recording) { if (config.getSettings().localRecording) { - recording.getContactSheet().ifPresent(f -> new Thread(() -> DesktopIntegration.open(f)).start()); + recording.getContactSheet().ifPresent(f -> GlobalThreadPool.submit(() -> DesktopIntegration.open(f))); } else { - recording.getContactSheet().ifPresent(f -> new Thread(() -> { + recording.getContactSheet().ifPresent(f -> GlobalThreadPool.submit(() -> { File target; try { target = File.createTempFile("cs_", ".jpg"); @@ -516,7 +517,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) { Dialogs.showError(getTabPane().getScene(), "Download Error", "An error occurred while downloading the contact sheet", e); } - }).start()); + })); } } @@ -526,7 +527,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { Optional newNote = Dialogs.showTextInput(source.getScene(), "Recording Notes", "", notes); if (newNote.isPresent()) { table.setCursor(Cursor.WAIT); - Thread backgroundThread = new Thread(() -> { + GlobalThreadPool.submit(() -> { List exceptions = new ArrayList<>(); try { recording.setNote(newNote.get()); @@ -542,13 +543,12 @@ public class RecordingsTab extends Tab implements TabSelectionListener { }); } }); - backgroundThread.start(); } } private void pin(List recordings) { table.setCursor(Cursor.WAIT); - Thread backgroundThread = new Thread(() -> { + GlobalThreadPool.submit(() -> { List exceptions = new ArrayList<>(); try { for (JavaFxRecording javaFxRecording : recordings) { @@ -569,12 +569,11 @@ public class RecordingsTab extends Tab implements TabSelectionListener { }); } }); - backgroundThread.start(); } private void unpin(List recordings) { table.setCursor(Cursor.WAIT); - Thread backgroundThread = new Thread(() -> { + GlobalThreadPool.submit(() -> { List exceptions = new ArrayList<>(); try { for (JavaFxRecording javaFxRecording : recordings) { @@ -595,7 +594,6 @@ public class RecordingsTab extends Tab implements TabSelectionListener { }); } }); - backgroundThread.start(); } private void jumpToNextModel(KeyCode code) { @@ -646,11 +644,11 @@ public class RecordingsTab extends Tab implements TabSelectionListener { private void onOpenDirectory(JavaFxRecording first) { File tsFile = first.getAbsoluteFile(); - new Thread(() -> DesktopIntegration.open(tsFile.getParent())).start(); + GlobalThreadPool.submit(() -> DesktopIntegration.open(tsFile.getParent())); } private void triggerPostProcessing(List recs) { - new Thread(() -> { + GlobalThreadPool.submit(() -> { for (JavaFxRecording rec : recs) { try { recorder.rerunPostProcessing(rec.getDelegate()); @@ -659,7 +657,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { LOG.error("Error while starting post-processing", e1); } } - }).start(); + }); } private void download(Recording recording) { @@ -761,12 +759,12 @@ public class RecordingsTab extends Tab implements TabSelectionListener { } private void play(Recording recording) { - new Thread(() -> { + GlobalThreadPool.submit(() -> { boolean started = Player.play(recording); if (started && Config.getInstance().getSettings().showPlayerStarting) { Platform.runLater(() -> Toast.makeText(getTabPane().getScene(), "Starting Player", 2000, 500, 500)); } - }).start(); + }); } private void play(Model model) { diff --git a/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java b/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java index 20f22702..18e58339 100644 --- a/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java +++ b/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java @@ -8,8 +8,6 @@ import java.util.Locale; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -107,7 +105,6 @@ public class ThumbCell extends StackPane { private ObservableList thumbCellList; private boolean mouseHovering = false; private boolean recording = false; - private static ExecutorService imageLoadingThreadPool = Executors.newFixedThreadPool(30); static LoadingCache resolutionCache = CacheBuilder.newBuilder() .expireAfterAccess(5, TimeUnit.MINUTES) .maximumSize(10000) @@ -388,7 +385,7 @@ public class ThumbCell extends StackPane { if (!Objects.equals(System.getenv("CTBREC_DEV"), "1")) { boolean updateThumbs = Config.getInstance().getSettings().updateThumbnails; if (updateThumbs || iv.getImage() == null) { - imageLoadingThreadPool.submit(createThumbDownload(url)); + GlobalThreadPool.submit(createThumbDownload(url)); } } } @@ -513,7 +510,7 @@ public class ThumbCell extends StackPane { void pauseResumeAction(boolean pause) { setCursor(Cursor.WAIT); - new Thread(() -> { + GlobalThreadPool.submit(() -> { try { if (pause) { recorder.suspendRecording(model); @@ -533,11 +530,11 @@ public class ThumbCell extends StackPane { } finally { Platform.runLater(() -> setCursor(Cursor.DEFAULT)); } - }).start(); + }); } private void startStopActionAsync(Model model, boolean start) { - new Thread(() -> { + GlobalThreadPool.submit(() -> { try { if (start) { recorder.addModel(model); @@ -553,7 +550,7 @@ public class ThumbCell extends StackPane { } finally { Platform.runLater(() -> setCursor(Cursor.DEFAULT)); } - }).start(); + }); } CompletableFuture follow(boolean follow) { diff --git a/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java b/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java index 25bd867d..eaa9bb33 100644 --- a/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java @@ -27,6 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Config; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.event.EventBusHolder; import ctbrec.recorder.Recorder; @@ -299,7 +300,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { return; } searchTask = new ThumbOverviewTabSearchTask(site, popover, popoverTreeList, newValue); - new Thread(searchTask).start(); + GlobalThreadPool.submit(searchTask); }; } @@ -602,7 +603,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { private MenuItem createTipMenuItem(ThumbCell cell) { MenuItem sendTip = new MenuItem("Send Tip"); sendTip.setOnAction(e -> { - TipDialog tipDialog = new TipDialog(getTabPane().getScene(), site, cell.getModel()); + TipDialog tipDialog = new TipDialog(getTabPane().getScene(), site); tipDialog.showAndWait(); String tipText = tipDialog.getResult(); if(tipText != null) { diff --git a/common/src/main/java/ctbrec/GlobalThreadPool.java b/common/src/main/java/ctbrec/GlobalThreadPool.java index aaeccf92..25f61e99 100644 --- a/common/src/main/java/ctbrec/GlobalThreadPool.java +++ b/common/src/main/java/ctbrec/GlobalThreadPool.java @@ -8,7 +8,7 @@ import java.util.concurrent.Future; public class GlobalThreadPool { - private static ExecutorService threadPool = Executors.newCachedThreadPool(r -> { + private static ExecutorService threadPool = Executors.newFixedThreadPool(30, r -> { Thread t = new Thread(r); t.setDaemon(true); t.setName("GlobalWorker-" + UUID.randomUUID().toString().substring(0, 8)); diff --git a/common/src/main/java/ctbrec/recorder/download/hls/MergedFfmpegHlsDownload.java b/common/src/main/java/ctbrec/recorder/download/hls/MergedFfmpegHlsDownload.java index ddc04067..a96868e6 100644 --- a/common/src/main/java/ctbrec/recorder/download/hls/MergedFfmpegHlsDownload.java +++ b/common/src/main/java/ctbrec/recorder/download/hls/MergedFfmpegHlsDownload.java @@ -82,9 +82,6 @@ public class MergedFfmpegHlsDownload extends AbstractHlsDownload { public void start() throws IOException { try { running = true; - Thread.currentThread().setName("Download " + model.getName()); - super.startTime = Instant.now(); - String segments = getSegmentPlaylistUrl(model); Files.createDirectories(targetFile.getParentFile().toPath()); diff --git a/common/src/main/java/ctbrec/sites/jasmin/LiveJasminTippingWebSocket.java b/common/src/main/java/ctbrec/sites/jasmin/LiveJasminTippingWebSocket.java index 19814722..1e82c9f6 100644 --- a/common/src/main/java/ctbrec/sites/jasmin/LiveJasminTippingWebSocket.java +++ b/common/src/main/java/ctbrec/sites/jasmin/LiveJasminTippingWebSocket.java @@ -11,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Config; +import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.io.HttpClient; import okhttp3.Request; @@ -78,9 +79,9 @@ public class LiveJasminTippingWebSocket { LOG.trace("relay <-- {} T{}", model.getName(), text); JSONObject event = new JSONObject(text); if (event.optString("event").equals("accept")) { - new Thread(() -> { + GlobalThreadPool.submit(() -> { sendToRelay("{\"event\":\"connectSharedObject\",\"name\":\"data/chat_so\"}"); - }).start(); + }); } else if(event.optString("event").equals("call")) { String func = event.optString("funcName"); if (func.equals("setName")) { diff --git a/server/src/main/java/ctbrec/recorder/server/HttpServer.java b/server/src/main/java/ctbrec/recorder/server/HttpServer.java index d9619ceb..b93c3897 100644 --- a/server/src/main/java/ctbrec/recorder/server/HttpServer.java +++ b/server/src/main/java/ctbrec/recorder/server/HttpServer.java @@ -1,5 +1,6 @@ package ctbrec.recorder.server; +import static java.nio.charset.StandardCharsets.*; import static javax.servlet.http.HttpServletResponse.*; import java.io.BufferedReader; @@ -53,6 +54,7 @@ import org.slf4j.LoggerFactory; import com.google.common.base.Objects; import ctbrec.Config; +import ctbrec.NotLoggedInExcetion; import ctbrec.Version; import ctbrec.event.EventBusHolder; import ctbrec.event.EventHandler; @@ -124,7 +126,7 @@ public class HttpServer { if (success) { LOG.info("Successfully logged in to {}", site.getName()); } else { - throw new RuntimeException("Login returned false"); + throw new NotLoggedInExcetion("Login returned false"); } } catch (Exception e) { LOG.info("Login to {} failed", site.getName()); @@ -243,7 +245,7 @@ public class HttpServer { byte[] hmac = Optional.ofNullable(HttpServer.this.config.getSettings().key).orElse(new byte[0]); try { JSONObject response = new JSONObject(); - response.put("hmac", new String(hmac, "utf-8")); + response.put("hmac", new String(hmac, UTF_8)); resp.getOutputStream().println(response.toString()); } catch (Exception e) { resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); diff --git a/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java b/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java index 44cb2b58..ca2b113e 100644 --- a/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java +++ b/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java @@ -1,23 +1,7 @@ package ctbrec.recorder.server; -import com.squareup.moshi.JsonAdapter; -import com.squareup.moshi.Moshi; -import ctbrec.Config; -import ctbrec.Model; -import ctbrec.Recording; -import ctbrec.io.BandwidthMeter; -import ctbrec.io.FileJsonAdapter; -import ctbrec.io.InstantJsonAdapter; -import ctbrec.io.ModelJsonAdapter; -import ctbrec.recorder.Recorder; -import ctbrec.sites.Site; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static javax.servlet.http.HttpServletResponse.*; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.security.InvalidKeyException; @@ -27,7 +11,27 @@ import java.util.Iterator; import java.util.List; import java.util.Objects; -import static javax.servlet.http.HttpServletResponse.*; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.Moshi; + +import ctbrec.Config; +import ctbrec.GlobalThreadPool; +import ctbrec.Model; +import ctbrec.Recording; +import ctbrec.io.BandwidthMeter; +import ctbrec.io.FileJsonAdapter; +import ctbrec.io.InstantJsonAdapter; +import ctbrec.io.ModelJsonAdapter; +import ctbrec.recorder.Recorder; +import ctbrec.sites.Site; public class RecorderServlet extends AbstractCtbrecServlet { @@ -87,24 +91,24 @@ public class RecorderServlet extends AbstractCtbrecServlet { resp.getWriter().write(response); break; case "stop": - new Thread(() -> { + GlobalThreadPool.submit(() -> { try { recorder.stopRecording(request.model); } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { LOG.error("Couldn't stop recording for model {}", request.model, e); } - }).start(); + }); response = "{\"status\": \"success\", \"msg\": \"Stopping recording\"}"; resp.getWriter().write(response); break; case "stopAt": - new Thread(() -> { + GlobalThreadPool.submit(() -> { try { recorder.stopRecordingAt(request.model); } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { LOG.error("Couldn't stop recording for model {}", request.model, e); } - }).start(); + }); response = "{\"status\": \"success\", \"msg\": \"Stopping recording\"}"; resp.getWriter().write(response); break; @@ -189,13 +193,13 @@ public class RecorderServlet extends AbstractCtbrecServlet { break; case "suspend": LOG.debug("Suspend recording for model {} - {}", request.model.getName(), request.model.getUrl()); - new Thread(() -> { + GlobalThreadPool.submit(() -> { try { recorder.suspendRecording(request.model); } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { LOG.error("Couldn't suspend recording for model {}", request.model, e); } - }).start(); + }); response = "{\"status\": \"success\", \"msg\": \"Suspending recording\"}"; resp.getWriter().write(response); break; From 159aacc8ff05bbc64726232a4588d6d78cdeb0ba Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sun, 10 Jan 2021 22:20:51 +0100 Subject: [PATCH 06/17] Use 16:9 format for all MFC thumbnails --- .../ui/sites/myfreecams/MyFreeCamsTabProvider.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/client/src/main/java/ctbrec/ui/sites/myfreecams/MyFreeCamsTabProvider.java b/client/src/main/java/ctbrec/ui/sites/myfreecams/MyFreeCamsTabProvider.java index 627cf296..e9c7a05d 100644 --- a/client/src/main/java/ctbrec/ui/sites/myfreecams/MyFreeCamsTabProvider.java +++ b/client/src/main/java/ctbrec/ui/sites/myfreecams/MyFreeCamsTabProvider.java @@ -26,19 +26,16 @@ public class MyFreeCamsTabProvider extends TabProvider { List tabs = new ArrayList<>(); PaginatedScheduledService updateService = new OnlineCamsUpdateService(); - ThumbOverviewTab online = new ThumbOverviewTab("Online", updateService, myFreeCams); - online.setRecorder(recorder); - tabs.add(online); + tabs.add(createTab("Online", updateService)); friends = new MyFreeCamsFriendsTab(myFreeCams); friends.setRecorder(recorder); + friends.setImageAspectRatio(9.0 / 16.0); + friends.preserveAspectRatioProperty().set(false); tabs.add(friends); updateService = new HDCamsUpdateService(); - ThumbOverviewTab hd = createTab("HD", updateService); - hd.setImageAspectRatio(9.0 / 16.0); - hd.preserveAspectRatioProperty().set(false); - tabs.add(hd); + tabs.add(createTab("HD", updateService)); updateService = new PopularModelService(); tabs.add(createTab("Most Popular", updateService)); @@ -54,6 +51,8 @@ public class MyFreeCamsTabProvider extends TabProvider { private ThumbOverviewTab createTab(String title, PaginatedScheduledService updateService) { ThumbOverviewTab tab = new ThumbOverviewTab(title, updateService, myFreeCams); + tab.setImageAspectRatio(9.0 / 16.0); + tab.preserveAspectRatioProperty().set(false); tab.setRecorder(recorder); return tab; } From beb325fee95a6d7f615cd1999f90b5e201ed9c60 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Tue, 12 Jan 2021 19:44:38 +0100 Subject: [PATCH 07/17] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98ef9b5f..e40ed045 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ 3.12.1 ======================== * Fix: "Resume all" started the recordings of models marked for later recording +* Fix: Login dialogs don't open +* Use 16:9 thumbnail format for MFC 3.12.0 ======================== From df37f4a61cbab9d1823d3b3fecd107b1942ccd74 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Tue, 12 Jan 2021 19:47:28 +0100 Subject: [PATCH 08/17] Set version to 3.12.1 --- client/pom.xml | 2 +- common/pom.xml | 2 +- master/pom.xml | 2 +- server/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index c97847b0..eb1ab3dc 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -8,7 +8,7 @@ ctbrec master - 3.12.0 + 3.12.1 ../master diff --git a/common/pom.xml b/common/pom.xml index 6b0fbee3..23cd5f57 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -8,7 +8,7 @@ ctbrec master - 3.12.0 + 3.12.1 ../master diff --git a/master/pom.xml b/master/pom.xml index 146e0658..40c9b84f 100644 --- a/master/pom.xml +++ b/master/pom.xml @@ -6,7 +6,7 @@ ctbrec master pom - 3.12.0 + 3.12.1 ../common diff --git a/server/pom.xml b/server/pom.xml index d9a98733..b7e53f48 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ ctbrec master - 3.12.0 + 3.12.1 ../master From 3d4bd6f331b9750ca1c7da99bd289104395fa58b Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Tue, 12 Jan 2021 20:42:08 +0100 Subject: [PATCH 09/17] Fix problem with Cam4 edge URLs The edge URLs are already abosulte, so the base URL does not have to be prepended --- CHANGELOG.md | 4 ++++ common/src/main/java/ctbrec/sites/cam4/Cam4Model.java | 10 +++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e40ed045..4fd86e8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +3.12.2 +======================== +* Fix: Some Cam4 URLs where broken + 3.12.1 ======================== * Fix: "Resume all" started the recordings of models marked for later recording diff --git a/common/src/main/java/ctbrec/sites/cam4/Cam4Model.java b/common/src/main/java/ctbrec/sites/cam4/Cam4Model.java index 5fcaebed..bf1ff46a 100644 --- a/common/src/main/java/ctbrec/sites/cam4/Cam4Model.java +++ b/common/src/main/java/ctbrec/sites/cam4/Cam4Model.java @@ -191,9 +191,13 @@ public class Cam4Model extends AbstractModel { StreamSource src = new StreamSource(); src.bandwidth = playlist.getStreamInfo().getBandwidth(); src.height = Optional.ofNullable(playlist.getStreamInfo()).map(StreamInfo::getResolution).map(res -> res.height).orElse(0); - String masterUrl = getPlaylistUrl(); - String baseUrl = masterUrl.substring(0, masterUrl.lastIndexOf('/') + 1); - src.mediaPlaylistUrl = baseUrl + playlist.getUri(); + if (playlist.getUri().startsWith("http")) { + src.mediaPlaylistUrl = playlist.getUri(); + } else { + String masterUrl = getPlaylistUrl(); + String baseUrl = masterUrl.substring(0, masterUrl.lastIndexOf('/') + 1); + src.mediaPlaylistUrl = baseUrl + playlist.getUri(); + } LOG.trace("Media playlist {}", src.mediaPlaylistUrl); sources.add(src); } From ed3a370d18bad49422c47f11a8423ff38f1f1a52 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Wed, 13 Jan 2021 20:22:11 +0100 Subject: [PATCH 10/17] Fix Cam4 search --- .../src/main/java/ctbrec/io/HttpClient.java | 59 +++++++++++++++---- .../src/main/java/ctbrec/sites/cam4/Cam4.java | 37 ++++++------ 2 files changed, 65 insertions(+), 31 deletions(-) diff --git a/common/src/main/java/ctbrec/io/HttpClient.java b/common/src/main/java/ctbrec/io/HttpClient.java index c069c2ac..42eee68a 100644 --- a/common/src/main/java/ctbrec/io/HttpClient.java +++ b/common/src/main/java/ctbrec/io/HttpClient.java @@ -1,15 +1,7 @@ package ctbrec.io; -import com.squareup.moshi.JsonAdapter; -import com.squareup.moshi.Moshi; -import ctbrec.Config; -import ctbrec.Settings.ProxyType; -import okhttp3.*; -import okhttp3.OkHttpClient.Builder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static java.nio.charset.StandardCharsets.*; -import javax.net.ssl.*; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -21,11 +13,40 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.TimeUnit; -import static java.nio.charset.StandardCharsets.UTF_8; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.Moshi; + +import ctbrec.Config; +import ctbrec.Settings.ProxyType; +import okhttp3.ConnectionPool; +import okhttp3.Cookie; +import okhttp3.Credentials; +import okhttp3.OkHttpClient; +import okhttp3.OkHttpClient.Builder; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.Route; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; public abstract class HttpClient { private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class); @@ -268,10 +289,22 @@ public abstract class HttpClient { } public static String bodyToJsonObject(Response response) { - return Optional.ofNullable(response.body()).map(Object::toString).orElse("{}"); + return Optional.ofNullable(response.body()).map(b -> { + try { + return b.string(); + } catch (IOException e) { + return "{}"; + } + }).orElse("{}"); } public static String bodyToJsonArray(Response response) { - return Optional.ofNullable(response.body()).map(Object::toString).orElse("[]"); + return Optional.ofNullable(response.body()).map(b -> { + try { + return b.string(); + } catch (IOException e) { + return "[]"; + } + }).orElse("[]"); } } diff --git a/common/src/main/java/ctbrec/sites/cam4/Cam4.java b/common/src/main/java/ctbrec/sites/cam4/Cam4.java index 2e21c239..12b9d14e 100644 --- a/common/src/main/java/ctbrec/sites/cam4/Cam4.java +++ b/common/src/main/java/ctbrec/sites/cam4/Cam4.java @@ -1,5 +1,18 @@ package ctbrec.sites.cam4; +import static ctbrec.io.HttpClient.*; +import static ctbrec.io.HttpConstants.*; + +import java.io.IOException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.json.JSONArray; +import org.json.JSONObject; + import ctbrec.Config; import ctbrec.Model; import ctbrec.StringUtil; @@ -8,18 +21,6 @@ import ctbrec.io.HttpException; import ctbrec.sites.AbstractSite; import okhttp3.Request; import okhttp3.Response; -import org.json.JSONArray; -import org.json.JSONObject; - -import java.io.IOException; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static ctbrec.io.HttpClient.bodyToJsonObject; -import static ctbrec.io.HttpConstants.USER_AGENT; public class Cam4 extends AbstractSite { @@ -121,27 +122,27 @@ public class Cam4 extends AbstractSite { private void search(String q, boolean offline, List models) throws IOException { String url = BASE_URI + "/usernameSearch?username=" + URLEncoder.encode(q, "utf-8"); - if(offline) { + if (offline) { url += "&offline=true"; } Request req = new Request.Builder() .url(url) .addHeader(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) .build(); - try(Response response = getHttpClient().execute(req)) { - if(response.isSuccessful()) { - String body = bodyToJsonObject(response); + try (Response response = getHttpClient().execute(req)) { + if (response.isSuccessful()) { + String body = bodyToJsonArray(response); JSONArray results = new JSONArray(body); for (int i = 0; i < results.length(); i++) { JSONObject result = results.getJSONObject(i); Model model = createModel(result.getString("username")); String thumb = null; - if(result.has("thumbnailId")) { + if (result.has("thumbnailId")) { thumb = "https://snapshots.xcdnpro.com/thumbnails/" + model.getName() + "?s=" + result.getString("thumbnailId"); } else { thumb = result.getString("profileImageLink"); } - if(StringUtil.isNotBlank(thumb)) { + if (StringUtil.isNotBlank(thumb)) { model.setPreview(thumb); } models.add(model); From 7678e6041e6c8861074ac0f9b74aa2defb79e713 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Wed, 13 Jan 2021 20:23:28 +0100 Subject: [PATCH 11/17] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fd86e8e..6db58796 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 3.12.2 ======================== * Fix: Some Cam4 URLs where broken +* Fix Cam4 search 3.12.1 ======================== From 505c5014286654fa7409fd06606f1019fc3e425c Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 16 Jan 2021 00:41:59 +0100 Subject: [PATCH 12/17] Stop hlsdl if the recording size didn't change for 90 seconds --- CHANGELOG.md | 3 ++- .../recorder/download/hls/HlsdlDownload.java | 26 ++++++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6db58796..3db32753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ 3.12.2 ======================== * Fix: Some Cam4 URLs where broken -* Fix Cam4 search +* Fix: Cam4 search didn't work +* Stop hlsdl if the recording size didn't change for 90 seconds 3.12.1 ======================== diff --git a/common/src/main/java/ctbrec/recorder/download/hls/HlsdlDownload.java b/common/src/main/java/ctbrec/recorder/download/hls/HlsdlDownload.java index 96aa0554..e3929aaf 100644 --- a/common/src/main/java/ctbrec/recorder/download/hls/HlsdlDownload.java +++ b/common/src/main/java/ctbrec/recorder/download/hls/HlsdlDownload.java @@ -1,18 +1,19 @@ package ctbrec.recorder.download.hls; import static ctbrec.recorder.download.StreamSource.*; +import static java.util.concurrent.TimeUnit.*; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.time.Duration; import java.time.Instant; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -41,6 +42,8 @@ public class HlsdlDownload extends AbstractDownload { protected transient Config config; protected transient Process hlsdlProcess; protected transient boolean running; + protected transient Instant lastSizeChange = Instant.now(); + protected transient long lastSize = 0; @Override public void init(Config config, Model model, Instant startTime) { @@ -60,22 +63,33 @@ public class HlsdlDownload extends AbstractDownload { Thread.currentThread().setName(threadName); Files.createDirectories(targetFile.getParentFile().toPath()); - Thread splittingMonitor = new Thread(() -> { + Thread processMonitor = new Thread(() -> { try { while (running) { if (splittingStrategy.splitNecessary(HlsdlDownload.this)) { LOG.debug("splitting strategy {} triggered split", splittingStrategy.getClass().getSimpleName()); stop(); } - TimeUnit.SECONDS.sleep(1); + long size = getSizeInByte(); + if (size != lastSize) { + lastSize = size; + lastSizeChange = Instant.now(); + } else { + int seconds = 90; + if (Duration.between(lastSizeChange, Instant.now()).toMillis() > SECONDS.toMillis(seconds)) { + LOG.info("Recording size didn't change for {} secs. Stopping recording for {}", seconds, model); + stop(); + } + } + SECONDS.sleep(1); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); - splittingMonitor.setName(threadName + " splitter"); - splittingMonitor.setDaemon(true); - splittingMonitor.start(); + processMonitor.setName(threadName + " monitor"); + processMonitor.setDaemon(true); + processMonitor.start(); Hlsdl hlsdl = new Hlsdl.Builder() .logOutput(config.getSettings().loghlsdlOutput) From f7b22ff57b84afe514e7a961d2afe95a65f4ef40 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 16 Jan 2021 12:12:36 +0100 Subject: [PATCH 13/17] Set version to 3.12.2 --- CHANGELOG.md | 2 +- client/pom.xml | 2 +- common/pom.xml | 2 +- master/pom.xml | 2 +- server/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3db32753..6ff84c2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ 3.12.2 ======================== -* Fix: Some Cam4 URLs where broken +* Fix: Some Cam4 URLs were broken * Fix: Cam4 search didn't work * Stop hlsdl if the recording size didn't change for 90 seconds diff --git a/client/pom.xml b/client/pom.xml index eb1ab3dc..072ed3f7 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -8,7 +8,7 @@ ctbrec master - 3.12.1 + 3.12.2 ../master diff --git a/common/pom.xml b/common/pom.xml index 23cd5f57..27d4fd5c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -8,7 +8,7 @@ ctbrec master - 3.12.1 + 3.12.2 ../master diff --git a/master/pom.xml b/master/pom.xml index 40c9b84f..8c6184b7 100644 --- a/master/pom.xml +++ b/master/pom.xml @@ -6,7 +6,7 @@ ctbrec master pom - 3.12.1 + 3.12.2 ../common diff --git a/server/pom.xml b/server/pom.xml index b7e53f48..78603c0b 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ ctbrec master - 3.12.1 + 3.12.2 ../master From 19da3c43bfb666d6477e53470255c31edbe52cb4 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 16 Jan 2021 15:05:28 +0100 Subject: [PATCH 14/17] Take into account all files of a recording to determine its size --- common/src/main/java/ctbrec/Recording.java | 47 +++++++++++--- .../ctbrec/recorder/NextGenLocalRecorder.java | 61 +++++++++++++------ 2 files changed, 81 insertions(+), 27 deletions(-) diff --git a/common/src/main/java/ctbrec/Recording.java b/common/src/main/java/ctbrec/Recording.java index f2ccc264..42be3998 100644 --- a/common/src/main/java/ctbrec/Recording.java +++ b/common/src/main/java/ctbrec/Recording.java @@ -3,6 +3,7 @@ package ctbrec; import static ctbrec.Recording.State.*; import java.io.File; +import java.io.IOException; import java.io.Serializable; import java.time.Duration; import java.time.Instant; @@ -13,6 +14,9 @@ import java.util.HashSet; import java.util.Optional; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import ctbrec.event.EventBusHolder; import ctbrec.event.RecordingStateChangedEvent; import ctbrec.io.IoUtils; @@ -20,6 +24,8 @@ import ctbrec.recorder.download.Download; import ctbrec.recorder.download.VideoLengthDetector; public class Recording implements Serializable { + private static final transient Logger LOG = LoggerFactory.getLogger(Recording.class); + private String id; private Model model; private transient Download download; @@ -239,22 +245,43 @@ public class Recording implements Serializable { } private long getSize() { - File rec = getAbsoluteFile(); - if (rec.isDirectory()) { - return IoUtils.getDirectorySize(rec); - } else { - if (!rec.exists()) { - if (rec.getName().endsWith(".m3u8")) { - return IoUtils.getDirectorySize(rec.getParentFile()); + try { + Set files = getAllRecordingFiles(); + long sum = 0; + for (File file : files) { + if (file.isDirectory()) { + sum += IoUtils.getDirectorySize(file); } else { - return -1; + if (!file.exists()) { + if (file.getName().endsWith(".m3u8")) { + sum += IoUtils.getDirectorySize(file.getParentFile()); + } + } else { + sum += file.length(); + } } - } else { - return rec.length(); } + return sum; + } catch (IOException e) { + LOG.error("Couldn't determine recording size", e); + return -1; } } + private Set getAllRecordingFiles() throws IOException { + Set files = new HashSet<>(); + if (absoluteFile != null) { + files.add(absoluteFile.getCanonicalFile()); + } + if (postProcessedFile != null) { + files.add(postProcessedFile.getCanonicalFile()); + } + for (String associatedFile : associatedFiles) { + files.add(new File(associatedFile).getCanonicalFile()); + } + return files; + } + public void refresh() { sizeInByte = getSize(); } diff --git a/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java b/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java index 7af9a570..d595f437 100644 --- a/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java +++ b/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java @@ -1,17 +1,7 @@ package ctbrec.recorder; -import com.google.common.eventbus.Subscribe; -import ctbrec.Config; -import ctbrec.Model; -import ctbrec.Recording; -import ctbrec.Recording.State; -import ctbrec.event.*; -import ctbrec.io.HttpClient; -import ctbrec.recorder.download.Download; -import ctbrec.recorder.postprocessing.PostProcessor; -import ctbrec.sites.Site; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static ctbrec.SubsequentAction.*; +import static ctbrec.event.Event.Type.*; import java.io.File; import java.io.IOException; @@ -21,14 +11,50 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.time.ZoneId; -import java.util.*; -import java.util.concurrent.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; -import static ctbrec.SubsequentAction.PAUSE; -import static ctbrec.SubsequentAction.REMOVE; -import static ctbrec.event.Event.Type.MODEL_ONLINE; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.Subscribe; + +import ctbrec.Config; +import ctbrec.Model; +import ctbrec.Recording; +import ctbrec.Recording.State; +import ctbrec.event.Event; +import ctbrec.event.EventBusHolder; +import ctbrec.event.ModelIsOnlineEvent; +import ctbrec.event.NoSpaceLeftEvent; +import ctbrec.event.RecordingStateChangedEvent; +import ctbrec.io.HttpClient; +import ctbrec.recorder.download.Download; +import ctbrec.recorder.postprocessing.PostProcessor; +import ctbrec.sites.Site; public class NextGenLocalRecorder implements Recorder { @@ -145,6 +171,7 @@ public class NextGenLocalRecorder implements Recorder { break; } } + recording.refresh(); setRecordingStatus(recording, State.FINISHED); recordingManager.saveRecording(recording); LOG.info("Post-processing finished for {}", recording.getModel().getName()); From 66d234e668d83bc7f360e2fff4fbcd18505ab655 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 16 Jan 2021 15:52:07 +0100 Subject: [PATCH 15/17] Remove restriction of download thread pool size (was 100 before) --- CHANGELOG.md | 5 +++++ .../src/main/java/ctbrec/recorder/NextGenLocalRecorder.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ff84c2f..2b46e195 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +3.12.3 +======================== +* Recording size now takes all associated files into account +* Removed restriction of download thread pool size (was 100 before) + 3.12.2 ======================== * Fix: Some Cam4 URLs were broken diff --git a/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java b/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java index d595f437..ff0e04a9 100644 --- a/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java +++ b/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java @@ -71,7 +71,7 @@ public class NextGenLocalRecorder implements Recorder { // thread pools for downloads and post-processing private BlockingQueue downloadQueue = new SynchronousQueue<>(); - private ThreadPoolExecutor downloadPool = new ThreadPoolExecutor(2, 100, 5, TimeUnit.MINUTES, downloadQueue, createThreadFactory("Download")); + private ThreadPoolExecutor downloadPool = new ThreadPoolExecutor(2, Integer.MAX_VALUE, 5, TimeUnit.MINUTES, downloadQueue, createThreadFactory("Download")); private ExecutorCompletionService completionService = new ExecutorCompletionService<>(downloadPool); private BlockingQueue ppQueue = new LinkedBlockingQueue<>(); From 368120e8e6246e8c5846f35e604018ba2b990196 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 16 Jan 2021 18:28:49 +0100 Subject: [PATCH 16/17] Add tab for recently watched models --- CHANGELOG.md | 1 + .../java/ctbrec/ui/CamrecApplication.java | 11 +- client/src/main/java/ctbrec/ui/Player.java | 3 + .../main/java/ctbrec/ui/ShutdownListener.java | 5 + .../ctbrec/ui/event/PlayerStartedEvent.java | 60 +++ .../java/ctbrec/ui/settings/SettingsTab.java | 3 + .../ctbrec/ui/tabs/RecentlyWatchedTab.java | 346 ++++++++++++++++++ .../main/java/ctbrec/ui/tabs/RecordedTab.java | 6 +- .../java/ctbrec/ui/tabs/RecordingsTab.java | 6 +- common/src/main/java/ctbrec/Settings.java | 1 + 10 files changed, 436 insertions(+), 6 deletions(-) create mode 100644 client/src/main/java/ctbrec/ui/ShutdownListener.java create mode 100644 client/src/main/java/ctbrec/ui/event/PlayerStartedEvent.java create mode 100644 client/src/main/java/ctbrec/ui/tabs/RecentlyWatchedTab.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b46e195..bfd0fd0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 3.12.3 ======================== +* Added "Recently watched" tab. Can be disabled in Settings -> General * Recording size now takes all associated files into account * Removed restriction of download thread pool size (was 100 before) diff --git a/client/src/main/java/ctbrec/ui/CamrecApplication.java b/client/src/main/java/ctbrec/ui/CamrecApplication.java index 1e860240..00c82846 100644 --- a/client/src/main/java/ctbrec/ui/CamrecApplication.java +++ b/client/src/main/java/ctbrec/ui/CamrecApplication.java @@ -63,6 +63,7 @@ import ctbrec.ui.news.NewsTab; import ctbrec.ui.settings.SettingsTab; import ctbrec.ui.tabs.DonateTabFx; import ctbrec.ui.tabs.HelpTab; +import ctbrec.ui.tabs.RecentlyWatchedTab; import ctbrec.ui.tabs.RecordedTab; import ctbrec.ui.tabs.RecordingsTab; import ctbrec.ui.tabs.SiteTab; @@ -216,6 +217,9 @@ public class CamrecApplication extends Application { tabPane.getTabs().add(modelsTab); recordingsTab = new RecordingsTab("Recordings", recorder, config); tabPane.getTabs().add(recordingsTab); + if (config.getSettings().recentlyWatched) { + tabPane.getTabs().add(new RecentlyWatchedTab(recorder, sites)); + } tabPane.getTabs().add(new SettingsTab(sites, recorder)); tabPane.getTabs().add(new NewsTab()); tabPane.getTabs().add(new DonateTabFx()); @@ -299,8 +303,11 @@ public class CamrecApplication extends Application { final boolean immediately = shutdownNow; new Thread(() -> { - modelsTab.saveState(); - recordingsTab.saveState(); + for (Tab tab : tabPane.getTabs()) { + if (tab instanceof ShutdownListener) { + ((ShutdownListener) tab).onShutdown(); + } + } onlineMonitor.shutdown(); recorder.shutdown(immediately); for (Site site : sites) { diff --git a/client/src/main/java/ctbrec/ui/Player.java b/client/src/main/java/ctbrec/ui/Player.java index 468ab883..34671404 100644 --- a/client/src/main/java/ctbrec/ui/Player.java +++ b/client/src/main/java/ctbrec/ui/Player.java @@ -25,10 +25,12 @@ import ctbrec.Config; import ctbrec.Model; import ctbrec.OS; import ctbrec.Recording; +import ctbrec.event.EventBusHolder; import ctbrec.io.StreamRedirector; import ctbrec.io.UrlUtil; import ctbrec.recorder.download.StreamSource; import ctbrec.ui.controls.Dialogs; +import ctbrec.ui.event.PlayerStartedEvent; import javafx.scene.Scene; public class Player { @@ -85,6 +87,7 @@ public class Player { } String playlistUrl = getPlaylistUrl(model); LOG.debug("Playing {}", playlistUrl); + EventBusHolder.BUS.post(new PlayerStartedEvent(model)); return Player.play(playlistUrl, async); } else { Dialogs.showError(scene, "Room not public", "Room is currently not public", null); diff --git a/client/src/main/java/ctbrec/ui/ShutdownListener.java b/client/src/main/java/ctbrec/ui/ShutdownListener.java new file mode 100644 index 00000000..cb5d95d4 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/ShutdownListener.java @@ -0,0 +1,5 @@ +package ctbrec.ui; + +public interface ShutdownListener { + void onShutdown(); +} diff --git a/client/src/main/java/ctbrec/ui/event/PlayerStartedEvent.java b/client/src/main/java/ctbrec/ui/event/PlayerStartedEvent.java new file mode 100644 index 00000000..6b040f60 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/event/PlayerStartedEvent.java @@ -0,0 +1,60 @@ +package ctbrec.ui.event; + +import java.time.Instant; +import java.util.Objects; + +import ctbrec.Model; +import ctbrec.ui.JavaFxModel; + +public class PlayerStartedEvent { + + private Model model; + private Instant timestamp; + + public PlayerStartedEvent(Model model) { + this(model, Instant.now()); + } + + public PlayerStartedEvent(Model model, Instant timestamp) { + this.model = unwrap(model); + this.timestamp = timestamp; + } + + public Model getModel() { + return model; + } + + public Instant getTimestamp() { + return timestamp; + } + + @Override + public int hashCode() { + return Objects.hash(timestamp); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PlayerStartedEvent other = (PlayerStartedEvent) obj; + return Objects.equals(timestamp, other.timestamp); + } + + @Override + public String toString() { + return "PlayerStartedEvent [model=" + model + ", timestamp=" + timestamp + "]"; + } + + private Model unwrap(Model model) { + if (model instanceof JavaFxModel) { + return ((JavaFxModel) model).getDelegate(); + } else { + return model; + } + } +} diff --git a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java index ed3452b9..368d992d 100644 --- a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java +++ b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java @@ -129,6 +129,7 @@ public class SettingsTab extends Tab implements TabSelectionListener { private SimpleBooleanProperty transportLayerSecurity; private SimpleBooleanProperty fastScrollSpeed; private SimpleBooleanProperty useHlsdl; + private SimpleBooleanProperty recentlyWatched; private SimpleFileProperty hlsdlExecutable; private ExclusiveSelectionProperty recordLocal; private SimpleIntegerProperty postProcessingThreads; @@ -191,6 +192,7 @@ public class SettingsTab extends Tab implements TabSelectionListener { confirmationDialogs = new SimpleBooleanProperty(null, "confirmationForDangerousActions", settings.confirmationForDangerousActions); useHlsdl = new SimpleBooleanProperty(null, "useHlsdl", settings.useHlsdl); hlsdlExecutable = new SimpleFileProperty(null, "hlsdlExecutable", settings.hlsdlExecutable); + recentlyWatched = new SimpleBooleanProperty(null, "recentlyWatched", settings.recentlyWatched); } private void createGui() { @@ -214,6 +216,7 @@ public class SettingsTab extends Tab implements TabSelectionListener { Setting.of("Display stream resolution in overview", determineResolution), Setting.of("Manually select stream quality", chooseStreamQuality, "Opens a dialog to select the video resolution before recording"), Setting.of("Enable live previews (experimental)", livePreviews), + Setting.of("Enable recently watched tab", recentlyWatched).needsRestart(), Setting.of("Add models from clipboard", monitorClipboard, "Monitor clipboard for model URLs and automatically add them to the recorder").needsRestart(), Setting.of("Fast scroll speed", fastScrollSpeed, "Makes the thumbnail overviews scroll faster with the mouse wheel").needsRestart(), Setting.of("Show confirmation dialogs", confirmationDialogs, "Show confirmation dialogs for irreversible actions"), diff --git a/client/src/main/java/ctbrec/ui/tabs/RecentlyWatchedTab.java b/client/src/main/java/ctbrec/ui/tabs/RecentlyWatchedTab.java new file mode 100644 index 00000000..b8f8f285 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/tabs/RecentlyWatchedTab.java @@ -0,0 +1,346 @@ +package ctbrec.ui.tabs; + +import static java.nio.charset.StandardCharsets.*; +import static java.nio.file.StandardOpenOption.*; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.time.Instant; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.Subscribe; +import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.Moshi; +import com.squareup.moshi.Types; + +import ctbrec.Config; +import ctbrec.Model; +import ctbrec.StringUtil; +import ctbrec.event.EventBusHolder; +import ctbrec.io.InstantJsonAdapter; +import ctbrec.io.ModelJsonAdapter; +import ctbrec.recorder.Recorder; +import ctbrec.sites.Site; +import ctbrec.ui.DesktopIntegration; +import ctbrec.ui.ShutdownListener; +import ctbrec.ui.action.FollowAction; +import ctbrec.ui.action.PlayAction; +import ctbrec.ui.action.StartRecordingAction; +import ctbrec.ui.controls.CustomMouseBehaviorContextMenu; +import ctbrec.ui.controls.DateTimeCellFactory; +import ctbrec.ui.controls.SearchBox; +import ctbrec.ui.event.PlayerStartedEvent; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.geometry.Insets; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.SelectionMode; +import javafx.scene.control.Tab; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableColumn.SortType; +import javafx.scene.control.TableView; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; +import javafx.scene.input.ContextMenuEvent; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.util.Callback; + +public class RecentlyWatchedTab extends Tab implements ShutdownListener { + + private static final Logger LOG = LoggerFactory.getLogger(RecentlyWatchedTab.class); + + private ObservableList filteredModels = FXCollections.observableArrayList(); + private ObservableList observableModels = FXCollections.observableArrayList(); + private TableView table = new TableView<>(); + private ContextMenu popup; + private ReentrantLock lock = new ReentrantLock(); + private Recorder recorder; + private List sites; + + public RecentlyWatchedTab(Recorder recorder, List sites) { + this.recorder = recorder; + this.sites = sites; + setText("Recently Watched"); + createGui(); + loadHistory(); + subscribeToPlayerEvents(); + setOnClosed(evt -> onShutdown()); + } + + private void createGui() { + BorderPane layout = new BorderPane(); + layout.setPadding(new Insets(5, 10, 10, 10)); + + SearchBox filterInput = new SearchBox(false); + filterInput.setPromptText("Filter"); + filterInput.textProperty().addListener( (observableValue, oldValue, newValue) -> { + String filter = filterInput.getText(); + lock.lock(); + try { + filter(filter); + } finally { + lock.unlock(); + } + }); + filterInput.getStyleClass().remove("search-box-icon"); + HBox.setHgrow(filterInput, Priority.ALWAYS); + HBox topBar = new HBox(5); + topBar.getChildren().addAll(filterInput); + layout.setTop(topBar); + BorderPane.setMargin(topBar, new Insets(0, 0, 5, 0)); + + table.setItems(observableModels); + table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); + table.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, event -> { + popup = createContextMenu(); + if (popup != null) { + popup.show(table, event.getScreenX(), event.getScreenY()); + } + event.consume(); + }); + table.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> { + if (popup != null) { + popup.hide(); + } + }); + table.addEventHandler(KeyEvent.KEY_PRESSED, event -> { + List selectedModels = table.getSelectionModel().getSelectedItems(); + if (event.getCode() == KeyCode.DELETE) { + delete(selectedModels); + } + }); + + int idx = 0; + TableColumn name = createTableColumn("Model", 200, idx++); + name.setId("name"); + name.setCellValueFactory(cdf -> new SimpleStringProperty(cdf.getValue().getModel().getDisplayName())); + name.setCellFactory(new ClickableCellFactory<>()); + table.getColumns().add(name); + + TableColumn url = createTableColumn("URL", 400, idx); + url.setCellValueFactory(cdf -> new SimpleStringProperty(cdf.getValue().getModel().getUrl())); + url.setCellFactory(new ClickableCellFactory<>()); + url.setEditable(false); + url.setId("url"); + table.getColumns().add(url); + + TableColumn timestamp = createTableColumn("Timestamp", 150, idx); + timestamp.setId("timestamp"); + timestamp.setCellValueFactory(cdf -> new SimpleObjectProperty(cdf.getValue().getTimestamp())); + timestamp.setCellFactory(new DateTimeCellFactory<>()); + timestamp.setEditable(false); + timestamp.setSortType(SortType.DESCENDING); + table.getColumns().add(timestamp); + table.getSortOrder().add(timestamp); + + ScrollPane scrollPane = new ScrollPane(); + scrollPane.setFitToHeight(true); + scrollPane.setFitToWidth(true); + scrollPane.setContent(table); + scrollPane.setStyle("-fx-background-color: -fx-background"); + layout.setCenter(scrollPane); + setContent(layout); + } + + private TableColumn createTableColumn(String text, int width, int idx) { + TableColumn tc = new TableColumn<>(text); + tc.setPrefWidth(width); + tc.setUserData(idx); + return tc; + } + + private void filter(String filter) { + lock.lock(); + try { + if (StringUtil.isBlank(filter)) { + observableModels.addAll(filteredModels); + filteredModels.clear(); + return; + } + + String[] tokens = filter.split(" "); + observableModels.addAll(filteredModels); + filteredModels.clear(); + for (int i = 0; i < table.getItems().size(); i++) { + StringBuilder sb = new StringBuilder(); + for (TableColumn tc : table.getColumns()) { + Object cellData = tc.getCellData(i); + if(cellData != null) { + String content = cellData.toString(); + sb.append(content).append(' '); + } + } + String searchText = sb.toString(); + + boolean tokensMissing = false; + for (String token : tokens) { + if(!searchText.toLowerCase().contains(token.toLowerCase())) { + tokensMissing = true; + break; + } + } + if(tokensMissing) { + PlayerStartedEvent sessionState = table.getItems().get(i); + filteredModels.add(sessionState); + } + } + observableModels.removeAll(filteredModels); + } finally { + lock.unlock(); + } + } + + private ContextMenu createContextMenu() { + ObservableList selectedRows = table.getSelectionModel().getSelectedItems(); + if (selectedRows.isEmpty()) { + return null; + } + + List selectedModels = selectedRows.stream().map(PlayerStartedEvent::getModel).collect(Collectors.toList()); + MenuItem copyUrl = new MenuItem("Copy URL"); + copyUrl.setOnAction(e -> { + Model selected = selectedModels.get(0); + final Clipboard clipboard = Clipboard.getSystemClipboard(); + final ClipboardContent content = new ClipboardContent(); + content.putString(selected.getUrl()); + clipboard.setContent(content); + }); + + MenuItem startRecording = new MenuItem("Start Recording"); + startRecording.setOnAction(e -> startRecording(selectedModels)); + MenuItem openInBrowser = new MenuItem("Open in Browser"); + openInBrowser.setOnAction(e -> DesktopIntegration.open(selectedModels.get(0).getUrl())); + MenuItem openInPlayer = new MenuItem("Open in Player"); + openInPlayer.setOnAction(e -> openInPlayer(selectedModels.get(0))); + MenuItem follow = new MenuItem("Follow"); + follow.setOnAction(e -> new FollowAction(getTabPane(), selectedModels).execute()); + MenuItem delete = new MenuItem("Delete"); + delete.setOnAction(e -> delete(selectedRows)); + + MenuItem clearHistory = new MenuItem("Clear history"); + clearHistory.setOnAction(e -> clearHistory()); + + ContextMenu menu = new CustomMouseBehaviorContextMenu(); + menu.getItems().addAll(startRecording, copyUrl, openInPlayer, openInBrowser, follow, delete, clearHistory); + + if (selectedModels.size() > 1) { + copyUrl.setDisable(true); + openInPlayer.setDisable(true); + openInBrowser.setDisable(true); + } + + return menu; + } + + private void clearHistory() { + observableModels.clear(); + filteredModels.clear(); + } + + private void delete(List selectedRows) { + observableModels.removeAll(selectedRows); + } + + private void startRecording(List selectedModels) { + new StartRecordingAction(getTabPane(), selectedModels, recorder).execute(); + } + + private void openInPlayer(Model selectedModel) { + new PlayAction(getTabPane(), selectedModel).execute(); + } + + private void subscribeToPlayerEvents() { + EventBusHolder.BUS.register(new Object() { + @Subscribe + public void handleEvent(PlayerStartedEvent evt) { + table.getItems().add(evt); + table.sort(); + } + }); + } + + private class ClickableCellFactory implements Callback, TableCell> { + @Override + public TableCell call(TableColumn param) { + TableCell cell = new TableCell<>() { + @Override + protected void updateItem(Object item, boolean empty) { + setText(empty ? "" : Objects.toString(item)); + } + }; + + cell.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> { + if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) { + Model selectedModel = table.getSelectionModel().getSelectedItem().getModel(); + if(selectedModel != null) { + new PlayAction(table, selectedModel).execute(); + } + } + }); + return cell; + } + } + + private void saveHistory() throws IOException { + Moshi moshi = new Moshi.Builder() + .add(Model.class, new ModelJsonAdapter(sites)) + .add(Instant.class, new InstantJsonAdapter()) + .build(); + Type type = Types.newParameterizedType(List.class, PlayerStartedEvent.class); + JsonAdapter> adapter = moshi.adapter(type); + String json = adapter.indent(" ").toJson(observableModels); + File recentlyWatched = new File(Config.getInstance().getConfigDir(), "recently_watched.json"); + LOG.debug("Saving recently watched models to {}", recentlyWatched.getAbsolutePath()); + Files.createDirectories(recentlyWatched.getParentFile().toPath()); + Files.write(recentlyWatched.toPath(), json.getBytes(UTF_8), CREATE, WRITE, TRUNCATE_EXISTING); + } + + private void loadHistory() { + File recentlyWatched = new File(Config.getInstance().getConfigDir(), "recently_watched.json"); + if(!recentlyWatched.exists()) { + return; + } + + LOG.debug("Loading recently watched models from {}", recentlyWatched.getAbsolutePath()); + Moshi moshi = new Moshi.Builder() + .add(Model.class, new ModelJsonAdapter(sites)) + .add(Instant.class, new InstantJsonAdapter()) + .build(); + Type type = Types.newParameterizedType(List.class, PlayerStartedEvent.class); + JsonAdapter> adapter = moshi.adapter(type); + try { + List fromJson = adapter.fromJson(Files.readString(recentlyWatched.toPath(), UTF_8)); + observableModels.addAll(fromJson); + } catch (IOException e) { + LOG.error("Couldn't load recently watched models", e); + } + } + + @Override + public void onShutdown() { + try { + saveHistory(); + } catch (IOException e) { + LOG.error("Couldn't safe recently watched models", e); + } + } +} diff --git a/client/src/main/java/ctbrec/ui/tabs/RecordedTab.java b/client/src/main/java/ctbrec/ui/tabs/RecordedTab.java index bc33c61a..38a112d4 100644 --- a/client/src/main/java/ctbrec/ui/tabs/RecordedTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/RecordedTab.java @@ -4,12 +4,13 @@ import java.util.List; import ctbrec.recorder.Recorder; import ctbrec.sites.Site; +import ctbrec.ui.ShutdownListener; import javafx.beans.value.ChangeListener; import javafx.geometry.Side; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; -public class RecordedTab extends Tab implements TabSelectionListener { +public class RecordedTab extends Tab implements TabSelectionListener, ShutdownListener { private TabPane tabPane; private RecordedModelsTab recordedModelsTab; @@ -54,7 +55,8 @@ public class RecordedTab extends Tab implements TabSelectionListener { } } - public void saveState() { + @Override + public void onShutdown() { recordedModelsTab.saveState(); recordLaterTab.saveState(); } diff --git a/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java b/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java index d7e51802..b8fda61e 100644 --- a/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java @@ -46,6 +46,7 @@ import ctbrec.ui.DesktopIntegration; import ctbrec.ui.FileDownload; import ctbrec.ui.JavaFxRecording; import ctbrec.ui.Player; +import ctbrec.ui.ShutdownListener; import ctbrec.ui.action.FollowAction; import ctbrec.ui.action.PauseAction; import ctbrec.ui.action.PlayAction; @@ -91,7 +92,7 @@ import javafx.scene.text.Font; import javafx.stage.FileChooser; import javafx.util.Duration; -public class RecordingsTab extends Tab implements TabSelectionListener { +public class RecordingsTab extends Tab implements TabSelectionListener, ShutdownListener { private static final String ERROR_WHILE_DOWNLOADING_RECORDING = "Error while downloading recording"; private static final Logger LOG = LoggerFactory.getLogger(RecordingsTab.class); @@ -822,7 +823,8 @@ public class RecordingsTab extends Tab implements TabSelectionListener { deleteThread.start(); } - public void saveState() { + @Override + public void onShutdown() { if (!table.getSortOrder().isEmpty()) { TableColumn col = table.getSortOrder().get(0); Config.getInstance().getSettings().recordingsSortColumn = col.getText(); diff --git a/common/src/main/java/ctbrec/Settings.java b/common/src/main/java/ctbrec/Settings.java index 70e59939..9fb7a42a 100644 --- a/common/src/main/java/ctbrec/Settings.java +++ b/common/src/main/java/ctbrec/Settings.java @@ -120,6 +120,7 @@ public class Settings { public String proxyPort; public ProxyType proxyType = ProxyType.DIRECT; public String proxyUser; + public boolean recentlyWatched = true; public double[] recordLaterColumnWidths = new double[0]; public String[] recordLaterColumnIds = new String[0]; public String recordLaterSortColumn = ""; From 0b9c953ac4aed468d4f0c815fe814b32c18460bd Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 16 Jan 2021 20:16:33 +0100 Subject: [PATCH 17/17] Set version to 3.13.0 --- CHANGELOG.md | 2 +- client/pom.xml | 2 +- common/pom.xml | 2 +- master/pom.xml | 2 +- server/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfd0fd0f..d42c1336 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -3.12.3 +3.13.0 ======================== * Added "Recently watched" tab. Can be disabled in Settings -> General * Recording size now takes all associated files into account diff --git a/client/pom.xml b/client/pom.xml index 072ed3f7..938984e5 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -8,7 +8,7 @@ ctbrec master - 3.12.2 + 3.13.0 ../master diff --git a/common/pom.xml b/common/pom.xml index 27d4fd5c..212955f3 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -8,7 +8,7 @@ ctbrec master - 3.12.2 + 3.13.0 ../master diff --git a/master/pom.xml b/master/pom.xml index 8c6184b7..3314fdaf 100644 --- a/master/pom.xml +++ b/master/pom.xml @@ -6,7 +6,7 @@ ctbrec master pom - 3.12.2 + 3.13.0 ../common diff --git a/server/pom.xml b/server/pom.xml index 78603c0b..73e5a08c 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ ctbrec master - 3.12.2 + 3.13.0 ../master