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;